remove support for partial body fetching

We have no way to match up a partial fetch response if less than the number of
octets we requested were returned, due to the way IMAP responds to partial fetches.

Example: We request BODY[TEXT]<0.32>, but BODY[TEXT] is only 16 bytes,
         then we get back: BODY[TEXT]<0> {16}
         This leaves us with no way to find out what the original
         length request was.

Because of this and the fact that the server can return requested
values in any order, I am disabling partial fetches entirely.
fork
Brian White 12 years ago
parent cbdb0abbf1
commit 9a0a42c50c

@ -23,6 +23,7 @@ var CRLF = '\r\n',
},
RE_LITHEADER = /(?:((?:BODY\[.*\](?:<\d+>)?)?|[^ ]+) )?\{(\d+)\}$/i,
RE_UNRESP = /^\* (OK|PREAUTH|NO|BAD) (?:\[(.+)\] )?(.+)$/i,
//RE_ISPARTIAL = /<(\d+)>$/,
RE_CMD = /^([^ ]+)(?: |$)/,
RE_ISHEADER = /HEADER/,
REX_UNRESPDATA = XRegExp('^\\* (?:(?:(?<type>NAMESPACE) (?<personal>(?:NIL|\\((?:\\(.+\\))+\\))) (?<other>(?:NIL|\\((?:\\(.+\\))+\\))) (?<shared>(?:NIL|\\((?:\\(.+\\))+\\))))|(?:(?<type>FLAGS) \\((?<flags>.*)\\))|(?:(?<type>LIST|LSUB|XLIST) \\((?<flags>.*)\\) (?<delimiter>".+"|NIL) (?<mailbox>.+))|(?:(?<type>(SEARCH|SORT))(?: (?<results>.*))?)|(?:(?<type>STATUS) (?<mailbox>.+) \\((?<attributes>.*)\\))|(?:(?<type>CAPABILITY) (?<capabilities>.+))|(?:(?<type>BYE) (?:\\[(?<code>.+)\\] )?(?<message>.+)))$', 'i'),
@ -273,17 +274,10 @@ ImapConnection.prototype.connect = function(loginCb) {
litType = m[1];
indata.expect = (m ? parseInt(m[2], 10) : -1);
if (indata.expect > -1) {
/*if (RE_ISPARTIAL.test(litType))
litType = litType.replace(RE_ISPARTIAL, '<$1.' + indata.expect + '>');*/
if ((m = /\* (\d+) FETCH/i.exec(indata.line))
&& /^BODY\[/i.test(litType)) {
/*if (!requests[0].fetchers[litType].msg) {
msg = new ImapMessage();
msg.seqno = parseInt(m[1], 10);
var fetches = requests[0].fetchers[litType];
fetches.msg = msg;
for (f = 0, lenf = fetches.length; f < lenf; ++f)
fetches[f].emit('message', msg);
}*/
msg = new ImapMessage();
msg.seqno = parseInt(m[1], 10);
fetches = requests[0].fetchers[litType];
@ -935,7 +929,7 @@ ImapConnection.prototype._fetch = function(which, uids, options, what, cb) {
var toFetch = '', prefix, extensions, self = this,
parse = true, headers, key, stream,
opts = { markSeen: false, struct: true, size: false },
fetchers = {}, part;
fetchers = {};
if (typeof what === 'function') {
cb = what;
@ -1073,23 +1067,20 @@ ImapConnection.prototype._fetch = function(which, uids, options, what, cb) {
}
if (wp.body) {
key = pprefix;
if (wp.body === true) {
if (wp.body === true)
key += 'TEXT]';
part = key;
} else if (typeof wp.body.start === 'number'
&& wp.body.length === 'number') {
/*else if (typeof wp.body.start === 'number'
&& typeof wp.body.length === 'number') {
if (wp.body.start < 0)
throw new Error('Invalid `start` value: ' + wp.body.start);
else if (wp.body.length <= 0)
throw new Error('Invalid `length` value: ' + wp.body.length);
key += 'TEXT]<';
key += wp.body.start;
part = key;
part += '.';
part += wp.body.length;
key += '.';
key += wp.body.length;
key += '>';
part += '>';
} else
}*/ else
throw new Error('Invalid `body` value: ' + wp.body);
key = key.trim();
@ -1097,13 +1088,12 @@ ImapConnection.prototype._fetch = function(which, uids, options, what, cb) {
stream = new ImapFetch();
if (!fetchers[key]) {
fetchers[key] = [stream];
toFetch += part;
toFetch += ' ' + key;
} else
fetchers[key].push(stream);
if (!wp.headers && !wp.headersNot && typeof wp.cb === 'function')
wp.cb(stream);
stream = undefined;
part = undefined;
key = undefined;
}
} else {

@ -53,7 +53,7 @@ exports.parseNamespaces = function(str, literals) {
else {
result = exports.parseExpr(str, literals);
vals = [];
for (var i=0,len=result.length; i<len; ++i) {
for (var i = 0, len = result.length; i < len; ++i) {
var val = {
prefix: result[i][0],
delimiter: result[i][1]
@ -61,10 +61,10 @@ exports.parseNamespaces = function(str, literals) {
if (result[i].length > 2) {
// extension data
val.extensions = [];
for (var j=2,len2=result[i].length; j<len2; j+=2) {
for (var j = 2, len2 = result[i].length; j < len2; j += 2) {
val.extensions.push({
name: result[i][j],
flags: result[i][j+1]
flags: result[i][j + 1]
});
}
}
@ -78,21 +78,42 @@ exports.parseFetchBodies = function(str, literals) {
literals.lp = 0;
var result = exports.parseExpr(str, literals),
bodies;
for (var i=0,len=result.length; i<len; i+=2) {
for (var i = 0, len = result.length; i < len; i += 2) {
if (Array.isArray(result[i])) {
if (result[i].length === 0)
result[i].push('');
else if (result[i].length > 1) {
//else if (result[i].length === 2 && typeof result[i][1] === 'number') {
// OK, so ordinarily this is where we'd transform a partial fetch
// key so that it matches up with a key in our request's fetchers object
// but since IMAP sucks, we have no way to match up a partial fetch
// response if less than the number of octets we requested were returned.
//
// Example: We request BODY[TEXT]<0.32>, but BODY[TEXT] is only 16 bytes,
// then we get back: BODY[TEXT]<0> {16}
// This leaves us with no way to find out what the original
// length request was. ARGH!!!^%&#^@#$%&$!
// Because of this and the fact that the server can return requested
// values in any order, I am disabling partial fetches entirely.
// BODY[TEXT]<0.32>
/*result[i][0] += '<';
result[i][0] += result[i][1]; // starting octet
result[i][0] += '.';
if (typeof result[i + 1] === 'number')
result[i][0] += result[i + 1];
else if (typeof
result[i][0] += '>';*/
} else if (result[i].length > 1) {
// HEADER.FIELDS (foo)
result[i][0] += ' (';
result[i][0] += result[i].slice(1).join(' ');
result[i][0] += ')';
}
if (bodies === undefined)
bodies = ['BODY[' + result[i][0] + ']', result[i+1]];
bodies = ['BODY[' + result[i][0] + ']', result[i + 1]];
else {
bodies.push('BODY[' + result[i][0] + ']');
bodies.push(result[i+1]);
bodies.push(result[i + 1]);
}
}
}
@ -102,22 +123,22 @@ exports.parseFetchBodies = function(str, literals) {
exports.parseFetch = function(str, literals, fetchData) {
literals.lp = 0;
var result = exports.parseExpr(str, literals);
for (var i=0,len=result.length; i<len; i+=2) {
for (var i = 0, len = result.length; i < len; i += 2) {
if (Array.isArray(result[i]))
continue;
result[i] = result[i].toUpperCase();
if (result[i] === 'UID')
fetchData.uid = parseInt(result[i+1], 10);
fetchData.uid = parseInt(result[i + 1], 10);
else if (result[i] === 'INTERNALDATE')
fetchData.date = result[i+1];
fetchData.date = result[i + 1];
else if (result[i] === 'FLAGS')
fetchData.flags = result[i+1].filter(utils.isNotEmpty);
fetchData.flags = result[i + 1].filter(utils.isNotEmpty);
else if (result[i] === 'BODYSTRUCTURE')
fetchData.structure = exports.parseBodyStructure(result[i+1], literals);
fetchData.structure = exports.parseBodyStructure(result[i + 1], literals);
else if (result[i] === 'RFC822.SIZE')
fetchData.size = parseInt(result[i+1], 10);
fetchData.size = parseInt(result[i + 1], 10);
else if (typeof result[i] === 'string') // simple extensions
fetchData[result[i].toLowerCase()] = result[i+1];
fetchData[result[i].toLowerCase()] = result[i + 1];
}
};
@ -140,8 +161,8 @@ exports.parseBodyStructure = function(cur, literals, prefix, partID) {
if (partLen > next) {
if (Array.isArray(cur[next])) {
part.params = {};
for (i=0,len=cur[next].length; i<len; i+=2)
part.params[cur[next][i].toLowerCase()] = cur[next][i+1];
for (i = 0, len = cur[next].length; i < len; i += 2)
part.params[cur[next][i].toLowerCase()] = cur[next][i + 1];
} else
part.params = cur[next];
++next;
@ -168,8 +189,8 @@ exports.parseBodyStructure = function(cur, literals, prefix, partID) {
}
if (Array.isArray(cur[2])) {
part.params = {};
for (i=0,len=cur[2].length; i<len; i+=2)
part.params[cur[2][i].toLowerCase()] = cur[2][i+1];
for (i = 0, len = cur[2].length; i < len; i += 2)
part.params[cur[2][i].toLowerCase()] = cur[2][i + 1];
if (cur[1] === null)
++next;
}
@ -177,7 +198,7 @@ exports.parseBodyStructure = function(cur, literals, prefix, partID) {
// envelope
if (partLen > next && Array.isArray(cur[next])) {
part.envelope = {};
for (i=0,len=cur[next].length; i<len; ++i) {
for (i = 0, len = cur[next].length; i < len; ++i) {
if (i === 0)
part.envelope.date = cur[next][i];
else if (i === 1)
@ -186,7 +207,7 @@ exports.parseBodyStructure = function(cur, literals, prefix, partID) {
var val = cur[next][i];
if (Array.isArray(val)) {
var addresses = [], inGroup = false, curGroup;
for (var j=0,len2=val.length; j<len2; ++j) {
for (var j = 0, len2 = val.length; j < len2; ++j) {
if (val[j][3] === null) { // start group addresses
inGroup = true;
curGroup = {
@ -267,8 +288,10 @@ exports.parseStructExtra = function(part, partLen, cur, next) {
disposition.type = cur[next][0];
if (Array.isArray(cur[next][1])) {
disposition.params = {};
for (var i=0,len=cur[next][1].length; i<len; i+=2)
disposition.params[cur[next][1][i].toLowerCase()] = cur[next][1][i+1];
for (var i = 0, len = cur[next][1].length, key; i < len; i += 2) {
key = cur[next][1][i].toLowerCase();
disposition.params[key] = cur[next][1][i + 1];
}
}
} else if (cur[next] !== null)
disposition.type = cur[next];
@ -301,36 +324,47 @@ exports.parseStructExtra = function(part, partLen, cur, next) {
exports.parseExpr = function(o, literals, result, start) {
start = start || 0;
var inQuote = false, lastPos = start - 1, isTop = false;
var inQuote = false, lastPos = start - 1, isTop = false, inLitStart = false,
val;
if (!result)
result = [];
if (typeof o === 'string') {
o = { str: o };
isTop = true;
}
for (var i=start,len=o.str.length; i<len; ++i) {
for (var i = start, len = o.str.length; i < len; ++i) {
if (!inQuote) {
if (o.str[i] === '"')
inQuote = true;
else if (o.str[i] === ' ' || o.str[i] === ')' || o.str[i] === ']') {
if (i - (lastPos+1) > 0)
result.push(exports.convStr(o.str.substring(lastPos+1, i), literals));
if (i - (lastPos + 1) > 0) {
val = exports.convStr(o.str.substring(lastPos + 1, i), literals);
result.push(val);
}
if ((o.str[i] === ')' || o.str[i] === ']') && !isTop)
return i;
lastPos = i;
} else if ((o.str[i] === '(' || o.str[i] === '[')
/*&& (i === 0 || !isAlpha(o.str.charCodeAt(i-1)))*/) {
} else if ((o.str[i] === '(' || o.str[i] === '[')) {
var innerResult = [];
i = exports.parseExpr(o, literals, innerResult, i+1);
i = exports.parseExpr(o, literals, innerResult, i + 1);
lastPos = i;
result.push(innerResult);
}
}/* else if (i > 0 && o.str[i] === '<' && o.str[i - 1] === ']') {
lastPos = i;
inLitStart = true;
} else if (o.str[i] === '>' && inLitStart) {
val = exports.convStr(o.str.substring(lastPos + 1, i), literals);
result[result.length - 1].push(val);
inLitStart = false;
}*/
} else if (o.str[i] === '"' &&
(o.str[i-1] &&
(o.str[i-1] !== '\\' || (o.str[i-2] && o.str[i-2] === '\\'))))
(o.str[i - 1] &&
(o.str[i - 1] !== '\\'
|| (o.str[i - 2] && o.str[i - 2] === '\\')
)))
inQuote = false;
if (i+1 === len && len - (lastPos+1) > 0)
result.push(exports.convStr(o.str.substring(lastPos+1), literals));
if (i + 1 === len && len - (lastPos + 1) > 0)
result.push(exports.convStr(o.str.substring(lastPos + 1), literals));
}
return (isTop ? result : start);
};

Loading…
Cancel
Save