|
|
|
@ -4,7 +4,8 @@ var assert = require('assert'),
|
|
|
|
|
Socket = require('net').Socket,
|
|
|
|
|
EventEmitter = require('events').EventEmitter,
|
|
|
|
|
utf7 = require('utf7').imap,
|
|
|
|
|
MIMEParser = require('./mimeparser'),
|
|
|
|
|
// customized copy of XRegExp to deal with multiple variables of the same
|
|
|
|
|
// name
|
|
|
|
|
XRegExp = require('./xregexp').XRegExp;
|
|
|
|
|
|
|
|
|
|
var parsers = require('./imap.parsers'),
|
|
|
|
@ -19,9 +20,10 @@ var CRLF = '\r\n',
|
|
|
|
|
BOXSELECTING: 3,
|
|
|
|
|
BOXSELECTED: 4
|
|
|
|
|
},
|
|
|
|
|
RE_LITHEADER = /(?:((?:BODY\[.*\])?|[^ ]+) )?\{(\d+)\}$/i,
|
|
|
|
|
RE_LITHEADER = /(?:((?:BODY\[.*\](?:<\d+>)?)?|[^ ]+) )?\{(\d+)\}$/i,
|
|
|
|
|
RE_UNRESP = /^\* (OK|PREAUTH|NO|BAD) (?:\[(.+)\] )?(.+)$/i,
|
|
|
|
|
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'),
|
|
|
|
|
REX_UNRESPNUM = XRegExp('^\\* (?<num>\\d+) (?:(?<type>EXISTS)|(?<type>RECENT)|(?<type>EXPUNGE)|(?:(?<type>FETCH) \\((?<info>.*)\\)))$', 'i');
|
|
|
|
|
|
|
|
|
@ -31,18 +33,18 @@ var IDLE_NONE = 1,
|
|
|
|
|
IDLE_READY = 3,
|
|
|
|
|
IDLE_DONE = 4;
|
|
|
|
|
|
|
|
|
|
function ImapConnection (options) {
|
|
|
|
|
function ImapConnection(options) {
|
|
|
|
|
if (!(this instanceof ImapConnection))
|
|
|
|
|
return new ImapConnection(options);
|
|
|
|
|
EventEmitter.call(this);
|
|
|
|
|
|
|
|
|
|
this._options = {
|
|
|
|
|
username: '',
|
|
|
|
|
password: '',
|
|
|
|
|
host: 'localhost',
|
|
|
|
|
port: 143,
|
|
|
|
|
secure: false,
|
|
|
|
|
connTimeout: 10000, // connection timeout in msecs
|
|
|
|
|
username: options.username || options.user || '',
|
|
|
|
|
password: options.password || '',
|
|
|
|
|
host: options.host || 'localhost',
|
|
|
|
|
port: options.port || 143,
|
|
|
|
|
secure: options.secure || false,
|
|
|
|
|
connTimeout: options.connTimeout || 10000, // connection timeout in msecs
|
|
|
|
|
debug: false
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
@ -87,10 +89,9 @@ function ImapConnection (options) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
this._options = utils.extend(true, this._options, options);
|
|
|
|
|
|
|
|
|
|
if (typeof this._options.debug === 'function')
|
|
|
|
|
this.debug = this._options.debug;
|
|
|
|
|
if (typeof options.debug === 'function')
|
|
|
|
|
this.debug = options.debug;
|
|
|
|
|
else
|
|
|
|
|
this.debug = false;
|
|
|
|
|
|
|
|
|
@ -102,6 +103,7 @@ function ImapConnection (options) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inherits(ImapConnection, EventEmitter);
|
|
|
|
|
module.exports = ImapConnection;
|
|
|
|
|
exports.ImapConnection = ImapConnection;
|
|
|
|
|
|
|
|
|
|
ImapConnection.prototype.connect = function(loginCb) {
|
|
|
|
@ -207,19 +209,13 @@ ImapConnection.prototype.connect = function(loginCb) {
|
|
|
|
|
if (b.length === 0 || b.p >= b.length) return;
|
|
|
|
|
self.debug&&self.debug('\n<== ' + inspect(b.toString('binary', b.p)) + '\n');
|
|
|
|
|
|
|
|
|
|
var r, m, litType, i, len, msg;
|
|
|
|
|
var r, m, litType, i, len, msg, fetches;
|
|
|
|
|
if (indata.expect > 0) {
|
|
|
|
|
r = read(b);
|
|
|
|
|
if (indata.streaming) {
|
|
|
|
|
if (requests[0]._useParser)
|
|
|
|
|
state.parser.execute(r);
|
|
|
|
|
else
|
|
|
|
|
requests[0]._msg.emit('data', r);
|
|
|
|
|
if (indata.expect === 0) {
|
|
|
|
|
requests[0].msg.emit('data', r);
|
|
|
|
|
if (indata.expect === 0)
|
|
|
|
|
indata.streaming = false;
|
|
|
|
|
if (requests[0]._useParser)
|
|
|
|
|
state.parser.finish();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (indata.temp)
|
|
|
|
|
indata.temp += r.toString('binary');
|
|
|
|
@ -244,36 +240,20 @@ ImapConnection.prototype.connect = function(loginCb) {
|
|
|
|
|
indata.line = r;
|
|
|
|
|
if (m)
|
|
|
|
|
litType = m[1];
|
|
|
|
|
//assert((litType = m[1]) !== undefined);
|
|
|
|
|
indata.expect = (m ? parseInt(m[2], 10) : -1);
|
|
|
|
|
if (indata.expect > -1) {
|
|
|
|
|
if ((m = /\* (\d+) FETCH/i.exec(indata.line))
|
|
|
|
|
&& /^BODY\[/i.test(litType)) {
|
|
|
|
|
msg = new ImapMessage();
|
|
|
|
|
msg.seqno = parseInt(m[1], 10);
|
|
|
|
|
requests[0]._msg = msg;
|
|
|
|
|
requests[0]._fetcher.emit('message', msg);
|
|
|
|
|
indata.streaming = true;
|
|
|
|
|
indata.literals.push(indata.expect);
|
|
|
|
|
if (requests[0]._useParser) {
|
|
|
|
|
requests[0]._msg.headers = {};
|
|
|
|
|
if (!state.parser) {
|
|
|
|
|
state.parser = new MIMEParser();
|
|
|
|
|
state.parser.on('header', function(name, val) {
|
|
|
|
|
name = name.toLowerCase();
|
|
|
|
|
if (requests[0]._headers
|
|
|
|
|
&& requests[0]._headers.indexOf(name) === -1)
|
|
|
|
|
return;
|
|
|
|
|
if (requests[0]._msg.headers[name] !== undefined)
|
|
|
|
|
requests[0]._msg.headers[name].push(val);
|
|
|
|
|
else
|
|
|
|
|
requests[0]._msg.headers[name] = [val];
|
|
|
|
|
});
|
|
|
|
|
state.parser.on('data', function(str) {
|
|
|
|
|
requests[0]._msg.emit('data', str);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
requests[0].msg = msg;
|
|
|
|
|
requests[0].key = litType;
|
|
|
|
|
var fetches = requests[0].fetchers[litType];
|
|
|
|
|
for (var f = 0, lenf = fetches.length; f < lenf; ++f)
|
|
|
|
|
fetches[f].emit('message', msg);
|
|
|
|
|
indata.streaming = (!RE_ISHEADER.test(litType));
|
|
|
|
|
if (indata.streaming)
|
|
|
|
|
indata.literals.push(indata.expect);
|
|
|
|
|
} else if (indata.expect === 0)
|
|
|
|
|
indata.literals.push('');
|
|
|
|
|
// start reading of the literal or get the rest of the response
|
|
|
|
@ -292,13 +272,14 @@ ImapConnection.prototype.connect = function(loginCb) {
|
|
|
|
|
switch (m.type) {
|
|
|
|
|
case 'FETCH':
|
|
|
|
|
// m.info = message details
|
|
|
|
|
msg = (requests[0] && requests[0]._msg
|
|
|
|
|
? requests[0]._msg
|
|
|
|
|
var data, parsed, headers, f, lenf;
|
|
|
|
|
msg = (requests[0] && requests[0].msg
|
|
|
|
|
? requests[0].msg
|
|
|
|
|
: new ImapMessage());
|
|
|
|
|
parsers.parseFetch(m.info, indata.literals, msg);
|
|
|
|
|
|
|
|
|
|
if (typeof msg.body === 'number') {
|
|
|
|
|
// we streamed a body
|
|
|
|
|
// we streamed a body, e.g. {3}\r\nfoo
|
|
|
|
|
delete msg.body;
|
|
|
|
|
msg.emit('end');
|
|
|
|
|
} else {
|
|
|
|
@ -308,18 +289,41 @@ ImapConnection.prototype.connect = function(loginCb) {
|
|
|
|
|
self.emit('msgupdate', msg);
|
|
|
|
|
else {
|
|
|
|
|
if (typeof msg.body === 'string') {
|
|
|
|
|
// a body was given as a non-literal string
|
|
|
|
|
var data = new Buffer(msg.body, 'binary');
|
|
|
|
|
delete msg.body;
|
|
|
|
|
requests[0]._fetcher.emit('message', msg);
|
|
|
|
|
msg.emit('data', data);
|
|
|
|
|
msg.emit('end');
|
|
|
|
|
// a body was given as a non-literal string, e.g. "foo"
|
|
|
|
|
fetches = requests[0].fetchers[requests[0].key];
|
|
|
|
|
if (RE_ISHEADER.test(requests[0].key)) {
|
|
|
|
|
var parsed, data, headers;
|
|
|
|
|
for (f = 0, lenf = fetches.length; f < lenf; ++f) {
|
|
|
|
|
if (fetches[f]._parse) {
|
|
|
|
|
if (parsed === undefined)
|
|
|
|
|
parsed = parsers.parseHeaders(msg.body);
|
|
|
|
|
headers = parsed;
|
|
|
|
|
} else {
|
|
|
|
|
if (data === undefined)
|
|
|
|
|
data = new Buffer(msg.body, 'binary');
|
|
|
|
|
headers = data;
|
|
|
|
|
}
|
|
|
|
|
delete msg.body;
|
|
|
|
|
msg.emit('headers', headers);
|
|
|
|
|
msg.emit('end');
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
var data = new Buffer(msg.body, 'binary');
|
|
|
|
|
delete msg.body;
|
|
|
|
|
for (f = 0, lenf = fetches.length; f < lenf; ++f) {
|
|
|
|
|
msg.emit('data', data);
|
|
|
|
|
msg.emit('end');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// non-body fetch
|
|
|
|
|
if (Object.keys(msg).indexOf('body') > -1)
|
|
|
|
|
if ('body' in msg)
|
|
|
|
|
delete msg.body;
|
|
|
|
|
requests[0]._fetcher.emit('message', msg);
|
|
|
|
|
msg.emit('end');
|
|
|
|
|
fetches = requests[0].fetchers[''];
|
|
|
|
|
for (f = 0, lenf = fetches.length; f < lenf; ++f) {
|
|
|
|
|
fetches[f].emit('message', msg);
|
|
|
|
|
msg.emit('end');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -777,27 +781,33 @@ ImapConnection.prototype.append = function(data, options, cb) {
|
|
|
|
|
options = {};
|
|
|
|
|
}
|
|
|
|
|
options = options || {};
|
|
|
|
|
if (!('mailbox' in options)) {
|
|
|
|
|
if (!options.mailbox) {
|
|
|
|
|
if (this._state.status !== STATES.BOXSELECTED)
|
|
|
|
|
throw new Error('No mailbox specified or currently selected');
|
|
|
|
|
else
|
|
|
|
|
options.mailbox = this._state.box.name;
|
|
|
|
|
}
|
|
|
|
|
var cmd = 'APPEND "' + utils.escape(options.mailbox) + '"';
|
|
|
|
|
if ('flags' in options) {
|
|
|
|
|
if (options.flags) {
|
|
|
|
|
if (!Array.isArray(options.flags))
|
|
|
|
|
options.flags = [options.flags];
|
|
|
|
|
cmd += " (\\" + options.flags.join(' \\') + ")";
|
|
|
|
|
}
|
|
|
|
|
if ('date' in options) {
|
|
|
|
|
if (options.date) {
|
|
|
|
|
if (!(options.date instanceof Date))
|
|
|
|
|
throw new Error('Expected null or Date object for date');
|
|
|
|
|
cmd += ' "' + options.date.getDate() + '-'
|
|
|
|
|
+ utils.MONTHS[options.date.getMonth()]
|
|
|
|
|
+ '-' + options.date.getFullYear();
|
|
|
|
|
cmd += ' ' + ('0' + options.date.getHours()).slice(-2) + ':'
|
|
|
|
|
+ ('0' + options.date.getMinutes()).slice(-2) + ':'
|
|
|
|
|
+ ('0' + options.date.getSeconds()).slice(-2);
|
|
|
|
|
cmd += ' "';
|
|
|
|
|
cmd += options.date.getDate();
|
|
|
|
|
cmd += '-';
|
|
|
|
|
cmd += utils.MONTHS[options.date.getMonth()];
|
|
|
|
|
cmd += '-';
|
|
|
|
|
cmd += options.date.getFullYear();
|
|
|
|
|
cmd += ' ';
|
|
|
|
|
cmd += ('0' + options.date.getHours()).slice(-2);
|
|
|
|
|
cmd += ':';
|
|
|
|
|
cmd += ('0' + options.date.getMinutes()).slice(-2);
|
|
|
|
|
cmd += ':';
|
|
|
|
|
cmd += ('0' + options.date.getSeconds()).slice(-2);
|
|
|
|
|
cmd += ((options.date.getTimezoneOffset() > 0) ? ' -' : ' +' );
|
|
|
|
|
cmd += ('0' + (-options.date.getTimezoneOffset() / 60)).slice(-2);
|
|
|
|
|
cmd += ('0' + (-options.date.getTimezoneOffset() % 60)).slice(-2);
|
|
|
|
@ -873,14 +883,11 @@ ImapConnection.prototype._sort = function(which, sorts, options, cb) {
|
|
|
|
|
+ utils.buildSearchQuery(options, this.capabilities), cb);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ImapConnection.prototype.fetch = function(uids, options) {
|
|
|
|
|
return this._fetch('UID ', uids, options);
|
|
|
|
|
ImapConnection.prototype.fetch = function(uids, options, what, cb) {
|
|
|
|
|
return this._fetch('UID ', uids, options, what);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ImapConnection.prototype._fetch = function(which, uids, options) {
|
|
|
|
|
if (this._state.status !== STATES.BOXSELECTED)
|
|
|
|
|
throw new Error('No mailbox is currently selected');
|
|
|
|
|
|
|
|
|
|
ImapConnection.prototype._fetch = function(which, uids, options, what, cb) {
|
|
|
|
|
if (uids === undefined || uids === null
|
|
|
|
|
|| (Array.isArray(uids) && uids.length === 0))
|
|
|
|
|
throw new Error('Nothing to fetch');
|
|
|
|
@ -889,60 +896,168 @@ ImapConnection.prototype._fetch = function(which, uids, options) {
|
|
|
|
|
uids = [uids];
|
|
|
|
|
utils.validateUIDList(uids);
|
|
|
|
|
|
|
|
|
|
var opts = {
|
|
|
|
|
markSeen: false,
|
|
|
|
|
request: {
|
|
|
|
|
struct: true,
|
|
|
|
|
headers: true,
|
|
|
|
|
body: false
|
|
|
|
|
}
|
|
|
|
|
}, toFetch, bodyRange, extensions, useParser, onlyHeaders, self = this;
|
|
|
|
|
var toFetch = '', prefix, extensions, self = this,
|
|
|
|
|
parse = true, headers, key, stream,
|
|
|
|
|
opts = { markSeen: false, size: false },
|
|
|
|
|
fetchers = {}, part;
|
|
|
|
|
|
|
|
|
|
if (typeof options !== 'object')
|
|
|
|
|
options = {};
|
|
|
|
|
utils.extend(true, opts, options);
|
|
|
|
|
|
|
|
|
|
if (Array.isArray(opts.request.body)) {
|
|
|
|
|
var rangeInfo;
|
|
|
|
|
if (opts.request.body.length !== 2)
|
|
|
|
|
throw new Error("Expected Array of length 2 for body byte range");
|
|
|
|
|
else if (typeof opts.request.body[1] !== 'string'
|
|
|
|
|
|| !(rangeInfo = /^([\d]+)\-([\d]+)$/.exec(opts.request.body[1]))
|
|
|
|
|
|| parseInt(rangeInfo[1], 10) >= parseInt(rangeInfo[2], 10))
|
|
|
|
|
throw new Error("Invalid body byte range format");
|
|
|
|
|
bodyRange = '<' + parseInt(rangeInfo[1], 10) + '.'
|
|
|
|
|
+ parseInt(rangeInfo[2], 10) + '>';
|
|
|
|
|
opts.request.body = opts.request.body[0];
|
|
|
|
|
if (typeof what === 'function') {
|
|
|
|
|
cb = what;
|
|
|
|
|
what = options;
|
|
|
|
|
} else {
|
|
|
|
|
if (options.markSeen)
|
|
|
|
|
opts.markSeen = true;
|
|
|
|
|
if (options.size)
|
|
|
|
|
opts.size = true;
|
|
|
|
|
}
|
|
|
|
|
if (opts.request.headers !== false
|
|
|
|
|
&& typeof opts.request.body === 'boolean') {
|
|
|
|
|
if (Array.isArray(opts.request.headers))
|
|
|
|
|
onlyHeaders = opts.request.headers.join(' ').toUpperCase();
|
|
|
|
|
if (opts.request.body === true) {
|
|
|
|
|
// fetches the whole entire message (including some/all headers)
|
|
|
|
|
toFetch = '';
|
|
|
|
|
} else if (onlyHeaders) {
|
|
|
|
|
// fetch specific headers only
|
|
|
|
|
toFetch = 'HEADER.FIELDS (' + onlyHeaders + ')';
|
|
|
|
|
prefix = (opts.markSeen ? ' BODY.PEEK[' : ' BODY[');
|
|
|
|
|
if (!Array.isArray(what))
|
|
|
|
|
what = [what];
|
|
|
|
|
for (var i = 0, wp, pprefix, len = what.length; i < len; ++i) {
|
|
|
|
|
wp = what[i];
|
|
|
|
|
if (wp.id !== undefined && !/^(?:[\d]+[\.]{0,1})*[\d]+$/.test(''+wp.id))
|
|
|
|
|
throw new Error('Invalid part id: ' + wp.id);
|
|
|
|
|
if (( (wp.headers
|
|
|
|
|
&& (!wp.headers.fields
|
|
|
|
|
|| (Array.isArray(wp.headers.fields)
|
|
|
|
|
&& wp.headers.fields.length === 0)
|
|
|
|
|
)
|
|
|
|
|
&& wp.headers.parse === false
|
|
|
|
|
)
|
|
|
|
|
||
|
|
|
|
|
(wp.headersNot
|
|
|
|
|
&& (!wp.headersNot.fields
|
|
|
|
|
|| (Array.isArray(wp.headersNot.fields)
|
|
|
|
|
&& wp.headersNot.fields.length === 0)
|
|
|
|
|
)
|
|
|
|
|
&& wp.headersNot.parse === false
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
&& wp.body === true) {
|
|
|
|
|
key = prefix.trim();
|
|
|
|
|
if (wp.id !== undefined)
|
|
|
|
|
key += wp.id;
|
|
|
|
|
key += ']';
|
|
|
|
|
if (!fetchers[key]) {
|
|
|
|
|
fetchers[key] = [new ImapFetch()];
|
|
|
|
|
toFetch += ' ';
|
|
|
|
|
toFetch += key;
|
|
|
|
|
}
|
|
|
|
|
if (typeof wp.cb === 'function')
|
|
|
|
|
wp.cb(fetchers[key][0]);
|
|
|
|
|
key = undefined;
|
|
|
|
|
} else if (wp.headers || wp.headersNot || wp.body) {
|
|
|
|
|
pprefix = prefix;
|
|
|
|
|
if (wp.id !== undefined) {
|
|
|
|
|
pprefix += wp.id;
|
|
|
|
|
pprefix += '.';
|
|
|
|
|
}
|
|
|
|
|
if (wp.headers) {
|
|
|
|
|
key = pprefix.trim();
|
|
|
|
|
if (wp.headers === true)
|
|
|
|
|
key += 'HEADER]';
|
|
|
|
|
else {
|
|
|
|
|
if (Array.isArray(wp.headers))
|
|
|
|
|
headers = wp.headers;
|
|
|
|
|
else if (typeof wp.headers === 'object') {
|
|
|
|
|
if (!Array.isArray(wp.headers.fields)
|
|
|
|
|
&& typeof wp.headers.fields !== 'string')
|
|
|
|
|
throw new Error('Invalid `fields` property');
|
|
|
|
|
if (Array.isArray(wp.headers.fields))
|
|
|
|
|
headers = wp.headers.fields;
|
|
|
|
|
else
|
|
|
|
|
headers = [wp.headers.fields];
|
|
|
|
|
if (wp.headers.parse === false)
|
|
|
|
|
parse = false;
|
|
|
|
|
} else
|
|
|
|
|
throw new Error('Invalid `headers` value: ' + wp.headers);
|
|
|
|
|
key += 'HEADER.FIELDS (';
|
|
|
|
|
key += headers.join(' ').toUpperCase();
|
|
|
|
|
key += ')]';
|
|
|
|
|
}
|
|
|
|
|
} else if (wp.headersNot) {
|
|
|
|
|
key = pprefix.trim();
|
|
|
|
|
if (wp.headersNot === true)
|
|
|
|
|
key += 'HEADER]';
|
|
|
|
|
else {
|
|
|
|
|
if (Array.isArray(wp.headersNot))
|
|
|
|
|
headers = wp.headersNot;
|
|
|
|
|
else if (typeof wp.headersNot === 'object') {
|
|
|
|
|
if (!Array.isArray(wp.headersNot.fields)
|
|
|
|
|
&& typeof wp.headersNot.fields !== 'string')
|
|
|
|
|
throw new Error('Invalid `fields` property');
|
|
|
|
|
if (Array.isArray(wp.headersNot.fields))
|
|
|
|
|
headers = wp.headersNot.fields;
|
|
|
|
|
else
|
|
|
|
|
headers = [wp.headersNot.fields];
|
|
|
|
|
if (wp.headersNot.parse === false)
|
|
|
|
|
parse = false;
|
|
|
|
|
} else
|
|
|
|
|
throw new Error('Invalid `headersNot` value: ' + wp.headersNot);
|
|
|
|
|
key += 'HEADER.FIELDS.NOT (';
|
|
|
|
|
key += headers.join(' ').toUpperCase();
|
|
|
|
|
key += ')]';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (key) {
|
|
|
|
|
stream = new ImapFetch();
|
|
|
|
|
if (parse)
|
|
|
|
|
stream._parse = true;
|
|
|
|
|
if (!fetchers[key]) {
|
|
|
|
|
fetchers[key] = [stream];
|
|
|
|
|
toFetch += ' ';
|
|
|
|
|
toFetch += key;
|
|
|
|
|
} else
|
|
|
|
|
fetchers.push(stream);
|
|
|
|
|
if (typeof wp.cb === 'function')
|
|
|
|
|
wp.cb(stream);
|
|
|
|
|
key = undefined;
|
|
|
|
|
}
|
|
|
|
|
if (wp.body) {
|
|
|
|
|
key = pprefix;
|
|
|
|
|
if (wp.body === true) {
|
|
|
|
|
key += 'TEXT]';
|
|
|
|
|
part = key;
|
|
|
|
|
} else if (typeof wp.body.start === 'number'
|
|
|
|
|
&& 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 += '>';
|
|
|
|
|
part += '>';
|
|
|
|
|
} else
|
|
|
|
|
throw new Error('Invalid `body` value: ' + wp.body);
|
|
|
|
|
|
|
|
|
|
key = key.trim();
|
|
|
|
|
if (!stream)
|
|
|
|
|
stream = new ImapFetch();
|
|
|
|
|
if (!fetchers[key]) {
|
|
|
|
|
fetchers[key] = [stream];
|
|
|
|
|
toFetch += part;
|
|
|
|
|
} else
|
|
|
|
|
fetchers[key].push(stream);
|
|
|
|
|
if (!wp.headers && !wp.headersNot && typeof wp.cb === 'function')
|
|
|
|
|
wp.cb(stream);
|
|
|
|
|
stream = undefined;
|
|
|
|
|
part = undefined;
|
|
|
|
|
key = undefined;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// fetches (all) headers only
|
|
|
|
|
toFetch = 'HEADER';
|
|
|
|
|
// non-body fetches
|
|
|
|
|
stream = new ImapFetch();
|
|
|
|
|
if (fetchers[''])
|
|
|
|
|
fetchers[''].push(stream);
|
|
|
|
|
else
|
|
|
|
|
fetchers[''] = [stream];
|
|
|
|
|
if (typeof wp.cd === 'function')
|
|
|
|
|
wp.cb(stream);
|
|
|
|
|
}
|
|
|
|
|
useParser = true;
|
|
|
|
|
} else if (opts.request.body === true) {
|
|
|
|
|
// fetches the whole entire message text (minus the headers), including
|
|
|
|
|
// all message parts
|
|
|
|
|
toFetch = 'TEXT';
|
|
|
|
|
} else if (typeof opts.request.body === 'string') {
|
|
|
|
|
if (opts.request.body.toUpperCase() === 'FULL') {
|
|
|
|
|
// fetches the whole entire message (including the headers)
|
|
|
|
|
// NOTE: does NOT parse the headers!
|
|
|
|
|
toFetch = '';
|
|
|
|
|
} else if (/^([\d]+[\.]{0,1})*[\d]+$/.test(opts.request.body)) {
|
|
|
|
|
// specific message part identifier, e.g. '1', '2', '1.1', '1.2', etc
|
|
|
|
|
toFetch = opts.request.body;
|
|
|
|
|
} else
|
|
|
|
|
throw new Error("Invalid body partID format");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// always fetch GMail-specific bits of information when on GMail
|
|
|
|
@ -956,38 +1071,33 @@ ImapConnection.prototype._fetch = function(which, uids, options) {
|
|
|
|
|
if (extensions)
|
|
|
|
|
cmd += extensions;
|
|
|
|
|
cmd += 'UID FLAGS INTERNALDATE';
|
|
|
|
|
if (opts.request.struct)
|
|
|
|
|
if (options.struct)
|
|
|
|
|
cmd += ' BODYSTRUCTURE';
|
|
|
|
|
if (opts.request.size)
|
|
|
|
|
if (options.size)
|
|
|
|
|
cmd += ' RFC822.SIZE';
|
|
|
|
|
if (toFetch !== undefined) {
|
|
|
|
|
cmd += ' BODY';
|
|
|
|
|
if (!opts.markSeen)
|
|
|
|
|
cmd += '.PEEK';
|
|
|
|
|
cmd += '[';
|
|
|
|
|
if (toFetch)
|
|
|
|
|
cmd += toFetch;
|
|
|
|
|
cmd += ']';
|
|
|
|
|
if (bodyRange)
|
|
|
|
|
cmd += bodyRange;
|
|
|
|
|
}
|
|
|
|
|
cmd += ')';
|
|
|
|
|
|
|
|
|
|
this._send(cmd, function(e) {
|
|
|
|
|
var fetcher = self._state.requests[0]._fetcher;
|
|
|
|
|
if (e && fetcher)
|
|
|
|
|
fetcher.emit('error', e);
|
|
|
|
|
else if (e && !fetcher)
|
|
|
|
|
self.emit('error', e);
|
|
|
|
|
else if (fetcher)
|
|
|
|
|
fetcher.emit('end');
|
|
|
|
|
this._send(cmd, function(err) {
|
|
|
|
|
var keys = Object.keys(fetchers), k, lenk = keys.length, f, lenf,
|
|
|
|
|
fetches;
|
|
|
|
|
if (err) {
|
|
|
|
|
for (k = 0; k < lenk; ++k) {
|
|
|
|
|
fetches = fetchers[keys[k]];
|
|
|
|
|
for (f = 0, lenf = fetches.length; f < lenf; ++f)
|
|
|
|
|
fetches[f].emit('error', err);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (k = 0; k < lenk; ++k) {
|
|
|
|
|
fetches = fetchers[keys[k]];
|
|
|
|
|
for (f = 0, lenf = fetches.length; f < lenf; ++f)
|
|
|
|
|
fetches[f].emit('end');
|
|
|
|
|
}
|
|
|
|
|
cb(err);
|
|
|
|
|
});
|
|
|
|
|
var imapFetcher = new ImapFetch(),
|
|
|
|
|
req = this._state.requests[this._state.requests.length - 1];
|
|
|
|
|
req._fetcher = imapFetcher;
|
|
|
|
|
req._useParser = useParser;
|
|
|
|
|
if (Array.isArray(opts.request.headers))
|
|
|
|
|
req._headers = onlyHeaders.toLowerCase().split(' ');
|
|
|
|
|
return imapFetcher;
|
|
|
|
|
|
|
|
|
|
this._state.requests[this._state.requests.length - 1].fetchers = fetchers;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ImapConnection.prototype.addFlags = function(uids, flags, cb) {
|
|
|
|
@ -1151,8 +1261,8 @@ ImapConnection.prototype.__defineGetter__('seq', function() {
|
|
|
|
|
setLabels: function(seqnos, labels, cb) {
|
|
|
|
|
self._storeLabels('', seqnos, labels, '', cb);
|
|
|
|
|
},
|
|
|
|
|
fetch: function(seqnos, options) {
|
|
|
|
|
return self._fetch('', seqnos, options);
|
|
|
|
|
fetch: function(seqnos, options, what, cb) {
|
|
|
|
|
return self._fetch('', seqnos, options, what, cb);
|
|
|
|
|
},
|
|
|
|
|
search: function(options, cb) {
|
|
|
|
|
self._search('', options, cb);
|
|
|
|
@ -1347,8 +1457,17 @@ ImapConnection.prototype._send = function(cmdstr, cb, bypass) {
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function ImapMessage() {}
|
|
|
|
|
function ImapMessage() {
|
|
|
|
|
this.seqno = undefined;
|
|
|
|
|
this.uid = undefined;
|
|
|
|
|
this.flags = undefined;
|
|
|
|
|
this.date = undefined;
|
|
|
|
|
this.structure = undefined;
|
|
|
|
|
this.size = undefined;
|
|
|
|
|
}
|
|
|
|
|
inherits(ImapMessage, EventEmitter);
|
|
|
|
|
|
|
|
|
|
function ImapFetch() {}
|
|
|
|
|
function ImapFetch() {
|
|
|
|
|
this._parse = false;
|
|
|
|
|
}
|
|
|
|
|
inherits(ImapFetch, EventEmitter);
|
|
|
|
|