function ImapSimple(imap) { var self = this; self.imap = imap; // flag to determine whether we should suppress ECONNRESET from bubbling up to listener self.ending = false; // pass most node-imap `Connection` events through 1:1 ['alert', 'mail', 'expunge', 'uidvalidity', 'update', 'close', 'end'].forEach(function (event) { self.imap.on(event, self.emit.bind(self, event)); }); // special handling for `error` event self.imap.on('error', function (err) { // if .end() has been called and an 'ECONNRESET' error is received, don't bubble if (err && self.ending && (err.code.toUpperCase() === 'ECONNRESET')) { return; } self.emit('error', err); }); }
n/a
function connect(options, callback) { options = options || {}; options.imap = options.imap || {}; // support old connectTimeout config option. Remove in v2.0.0 if (options.hasOwnProperty('connectTimeout')) { console.warn('[imap-simple] connect: options.connectTimeout is deprecated. ' + 'Please use options.imap.authTimeout instead.'); options.imap.authTimeout = options.connectTimeout; } // set default authTimeout options.imap.authTimeout = options.imap.hasOwnProperty('authTimeout') ? options.imap.authTimeout : 2000; if (callback) { return nodeify(connect(options), callback); } return new Promise(function (resolve, reject) { var imap = new Imap(options.imap); function imapOnReady() { imap.removeListener('error', imapOnError); imap.removeListener('close', imapOnClose); imap.removeListener('end', imapOnEnd); resolve(new ImapSimple(imap)); } function imapOnError(err) { if (err.source === 'timeout-auth') { err = new errors.ConnectionTimeoutError(options.imap.authTimeout); } imap.removeListener('ready', imapOnReady); imap.removeListener('close', imapOnClose); imap.removeListener('end', imapOnEnd); reject(err); } function imapOnEnd() { imap.removeListener('ready', imapOnReady); imap.removeListener('error', imapOnError); imap.removeListener('close', imapOnClose); reject(new Error('Connection ended unexpectedly')); } function imapOnClose() { imap.removeListener('ready', imapOnReady); imap.removeListener('error', imapOnError); imap.removeListener('end', imapOnEnd); reject(new Error('Connection closed unexpectedly')); } imap.once('ready', imapOnReady); imap.once('error', imapOnError); imap.once('close', imapOnClose); imap.once('end', imapOnEnd); if (options.hasOwnProperty('onmail')) { imap.on('mail', options.onmail); } if (options.hasOwnProperty('onexpunge')) { imap.on('expunge', options.onexpunge); } if (options.hasOwnProperty('onupdate')) { imap.on('update', options.onupdate); } imap.connect(); }); }
...
host: 'imap.gmail.com',
port: 993,
tls: true,
authTimeout: 3000
}
};
imaps.connect(config).then(function (connection) {
return connection.openBox('INBOX').then(function () {
var searchCriteria = [
'UNSEEN'
];
var fetchOptions = {
...
function ConnectionTimeoutError(timeout) { Error.call(this); Error.captureStackTrace(this, this.constructor); this.message = 'connection timed out'; if (timeout) { this.message += '. timeout = ' + timeout + ' ms'; } this.name = 'ConnectionTimeoutError'; }
n/a
function getParts(struct, parts) { parts = parts || []; for (var i = 0; i < struct.length; i++) { if (Array.isArray(struct[i])) { getParts(struct[i], parts); } else if (struct[i].partID) { parts.push(struct[i]); } } return parts; }
...
// retrieve only the headers of the messages
return connection.search(searchCriteria, fetchOptions);
}).then(function (messages) {
var attachments = [];
messages.forEach(function (message) {
var parts = imaps.getParts(message.attributes.struct);
attachments = attachments.concat(parts.filter(function (part) {
return part.disposition && part.disposition.type === 'ATTACHMENT';
}).map(function (part) {
// retrieve the attachments only of the messages with attachments
return connection.getPartData(message, part)
.then(function (partData) {
return {
...
function parseHeader(str, noDecode) { var lines = str.split(RE_CRLF), len = lines.length, header = {}, state = { buffer: undefined, encoding: undefined, consecutive: false, replaces: undefined, curReplace: undefined, remainder: undefined }, m, h, i, val; for (i = 0; i < len; ++i) { if (lines[i].length === 0) break; // empty line separates message's header and body if (lines[i][0] === '\t' || lines[i][0] === ' ') { if (!Array.isArray(header[h])) continue; // ignore invalid first line // folded header content val = lines[i]; if (!noDecode) { if (RE_ENCWORD_END.test(lines[i - 1]) && RE_ENCWORD_BEGIN.test(val)) { // RFC2047 says to *ignore* leading whitespace in folded header values // for adjacent encoded-words ... val = val.substring(1); } } header[h][header[h].length - 1] += val; } else { m = RE_HDR.exec(lines[i]); if (m) { h = m[1].toLowerCase().trim(); if (m[2]) { if (header[h] === undefined) header[h] = [m[2]]; else header[h].push(m[2]); } else header[h] = ['']; } else break; } } if (!noDecode) { var hvs; for (h in header) { hvs = header[h]; for (i = 0, len = header[h].length; i < len; ++i) hvs[i] = decodeWords(hvs[i], state); } } return header; }
...
var part = {
which: info.which,
size: info.size,
body: body
};
if (isHeader.test(part.which)) {
part.body = Imap.parseHeader(part.body);
}
messageParts.push(part);
});
}
function messageOnAttributes(attrs) {
...
function ImapSimple(imap) { var self = this; self.imap = imap; // flag to determine whether we should suppress ECONNRESET from bubbling up to listener self.ending = false; // pass most node-imap `Connection` events through 1:1 ['alert', 'mail', 'expunge', 'uidvalidity', 'update', 'close', 'end'].forEach(function (event) { self.imap.on(event, self.emit.bind(self, event)); }); // special handling for `error` event self.imap.on('error', function (err) { // if .end() has been called and an 'ECONNRESET' error is received, don't bubble if (err && self.ending && (err.code.toUpperCase() === 'ECONNRESET')) { return; } self.emit('error', err); }); }
n/a
function EventEmitter() { EventEmitter.init.call(this); }
n/a
addFlags = function (uid, flags, callback) { var self = this; if (callback) { return nodeify(self.addFlags(uid, flags), callback); } return new Promise(function (resolve, reject) { self.imap.addFlags(uid, flags, function (err) { if (err) { reject(err); return; } resolve(); }); }); }
...
* @returns {undefined|Promise} Returns a promise when no callback is specified, resolving when the action succeeds.
* @memberof ImapSimple
*/
ImapSimple.prototype.addFlags = function (uid, flags, callback) {
var self = this;
if (callback) {
return nodeify(self.addFlags(uid, flags), callback);
}
return new Promise(function (resolve, reject) {
self.imap.addFlags(uid, flags, function (err) {
if (err) {
reject(err);
return;
...
addMessageLabel = function (source, labels, callback) { var self = this; if (callback) { return nodeify(self.addMessageLabel(source, labels), callback); } return new Promise(function (resolve, reject) { self.imap.addLabels(source, labels, function (err) { if (err) { reject(err); return; } resolve(); }); }); }
...
* @returns {undefined|Promise} Returns a promise when no callback is specified, resolving when the action succeeds.
* @memberof ImapSimple
*/
ImapSimple.prototype.addMessageLabel = function (source, labels, callback) {
var self = this;
if (callback) {
return nodeify(self.addMessageLabel(source, labels), callback);
}
return new Promise(function (resolve, reject) {
self.imap.addLabels(source, labels, function (err) {
if (err) {
reject(err);
return;
...
delFlags = function (uid, flags, callback) { var self = this; if (callback) { return nodeify(self.delFlags(uid, flags), callback); } return new Promise(function (resolve, reject) { self.imap.delFlags(uid, flags, function (err) { if (err) { reject(err); return; } resolve(); }); }); }
...
* @returns {undefined|Promise} Returns a promise when no callback is specified, resolving when the action succeeds.
* @memberof ImapSimple
*/
ImapSimple.prototype.delFlags = function (uid, flags, callback) {
var self = this;
if (callback) {
return nodeify(self.delFlags(uid, flags), callback);
}
return new Promise(function (resolve, reject) {
self.imap.delFlags(uid, flags, function (err) {
if (err) {
reject(err);
return;
...
end = function () { var self = this; // set state flag to suppress 'ECONNRESET' errors that are triggered when .end() is called. // it is a known issue that has no known fix. This just temporarily ignores that error. // https://github.com/mscdex/node-imap/issues/391 // https://github.com/mscdex/node-imap/issues/395 self.ending = true; // using 'close' event to unbind ECONNRESET error handler, because the node-imap // maintainer claims it is the more reliable event between 'end' and 'close'. // https://github.com/mscdex/node-imap/issues/394 self.imap.once('close', function () { self.ending = false; }); self.imap.end(); }
...
- made `ImapSimple` an event emitter
## 1.1.2 - 2015-03-02
#### Fixed
- Put ECONNRESET error in better place, and only ignored error when calling .end()
- 'ready' and 'error' event handlers will now only fire once when connecting
## 1.1.1 - 2015-02-27
#### Fixed
- Put in basic fix for ECONNRESET error when calling .end()
...
getPartData = function (message, part, callback) { var self = this; if (callback) { return nodeify(self.getPartData(message, part), callback); } return new Promise(function (resolve, reject) { var fetch = self.imap.fetch(message.attributes.uid, { bodies: [part.partID], struct: true }); function fetchOnMessage(msg) { getMessage(msg).then(function (result) { if (result.parts.length !== 1) { reject(new Error('Got ' + result.parts.length + ' parts, should get 1')); return; } var data = result.parts[0].body; var encoding = part.encoding.toUpperCase(); if (encoding === 'BASE64') { resolve(new Buffer(data, 'base64')); return; } if (encoding === 'QUOTED-PRINTABLE') { if (part.params && part.params.charset && part.params.charset.toUpperCase() === 'UTF-8') { resolve((new Buffer(utf8.decode(qp.decode(data)))).toString()); } else { resolve((new Buffer(qp.decode(data))).toString()); } return; } if (encoding === '7BIT') { resolve((new Buffer(data)).toString('ascii')); return; } if (encoding === '8BIT' || encoding === 'BINARY') { var charset = (part.params && part.params.charset) || 'utf-8'; resolve(iconvlite.decode(new Buffer(data), charset)); return; } // if it gets here, the encoding is not currently supported reject(new Error('Unknown encoding ' + part.encoding)); }); } function fetchOnError(err) { fetch.removeListener('message', fetchOnMessage); fetch.removeListener('end', fetchOnEnd); reject(err); } function fetchOnEnd() { fetch.removeListener('message', fetchOnMessage); fetch.removeListener('error', fetchOnError); } fetch.once('message', fetchOnMessage); fetch.once('error', fetchOnError); fetch.once('end', fetchOnEnd); }); }
...
messages.forEach(function (message) {
var parts = imaps.getParts(message.attributes.struct);
attachments = attachments.concat(parts.filter(function (part) {
return part.disposition && part.disposition.type === 'ATTACHMENT';
}).map(function (part) {
// retrieve the attachments only of the messages with attachments
return connection.getPartData(message, part)
.then(function (partData) {
return {
filename: part.disposition.params.filename,
data: partData
};
});
}));
...
moveMessage = function (source, boxName, callback) { var self = this; if (callback) { return nodeify(self.moveMessage(source, boxName), callback); } return new Promise(function (resolve, reject) { self.imap.move(source, boxName, function (err) { if (err) { reject(err); return; } resolve(); }); }); }
...
* @returns {undefined|Promise} Returns a promise when no callback is specified, resolving when the action succeeds.
* @memberof ImapSimple
*/
ImapSimple.prototype.moveMessage = function (source, boxName, callback) {
var self = this;
if (callback) {
return nodeify(self.moveMessage(source, boxName), callback);
}
return new Promise(function (resolve, reject) {
self.imap.move(source, boxName, function (err) {
if (err) {
reject(err);
return;
...
openBox = function (boxName, callback) { var self = this; if (callback) { return nodeify(this.openBox(boxName), callback); } return new Promise(function (resolve, reject) { self.imap.openBox(boxName, function (err, result) { if (err) { reject(err); return; } resolve(result); }); }); }
...
tls: true,
authTimeout: 3000
}
};
imaps.connect(config).then(function (connection) {
return connection.openBox('INBOX').then(function () {
var searchCriteria = [
'UNSEEN'
];
var fetchOptions = {
bodies: ['HEADER', 'TEXT'],
markSeen: false
...
search = function (searchCriteria, fetchOptions, callback) { var self = this; if (!callback && typeof fetchOptions === 'function') { callback = fetchOptions; fetchOptions = null; } if (callback) { return nodeify(this.search(searchCriteria, fetchOptions), callback); } return new Promise(function (resolve, reject) { self.imap.search(searchCriteria, function (err, uids) { if (err) { reject(err); return; } if (!uids.length) { resolve([]); return; } var fetch = self.imap.fetch(uids, fetchOptions); var messagesRetrieved = 0; var messages = []; function fetchOnMessage(message, seqNo) { getMessage(message).then(function (message) { message.seqNo = seqNo; messages[seqNo] = message; messagesRetrieved++; if (messagesRetrieved === uids.length) { fetchCompleted(); } }); } function fetchCompleted() { // pare array down while keeping messages in order var pared = messages.filter(function (m) { return !!m; }); resolve(pared); } function fetchOnError(err) { fetch.removeListener('message', fetchOnMessage); fetch.removeListener('end', fetchOnEnd); reject(err); } function fetchOnEnd() { fetch.removeListener('message', fetchOnMessage); fetch.removeListener('error', fetchOnError); } fetch.on('message', fetchOnMessage); fetch.once('error', fetchOnError); fetch.once('end', fetchOnEnd); }); }); }
...
];
var fetchOptions = {
bodies: ['HEADER', 'TEXT'],
markSeen: false
};
return connection.search(searchCriteria, fetchOptions).then(function (results
) {
var subjects = results.map(function (res) {
return res.parts.filter(function (part) {
return part.which === 'HEADER';
})[0].body.subject[0];
});
console.log(subjects);
...
function ConnectionTimeoutError(timeout) { Error.call(this); Error.captureStackTrace(this, this.constructor); this.message = 'connection timed out'; if (timeout) { this.message += '. timeout = ' + timeout + ' ms'; } this.name = 'ConnectionTimeoutError'; }
...
imap.removeListener('close', imapOnClose);
imap.removeListener('end', imapOnEnd);
resolve(new ImapSimple(imap));
}
function imapOnError(err) {
if (err.source === 'timeout-auth') {
err = new errors.ConnectionTimeoutError(options.imap.authTimeout);
}
imap.removeListener('ready', imapOnReady);
imap.removeListener('close', imapOnClose);
imap.removeListener('end', imapOnEnd);
reject(err);
}
...
function ConnectionTimeoutError(timeout) { Error.call(this); Error.captureStackTrace(this, this.constructor); this.message = 'connection timed out'; if (timeout) { this.message += '. timeout = ' + timeout + ' ms'; } this.name = 'ConnectionTimeoutError'; }
...
imap.removeListener('close', imapOnClose);
imap.removeListener('end', imapOnEnd);
resolve(new ImapSimple(imap));
}
function imapOnError(err) {
if (err.source === 'timeout-auth') {
err = new errors.ConnectionTimeoutError(options.imap.authTimeout);
}
imap.removeListener('ready', imapOnReady);
imap.removeListener('close', imapOnClose);
imap.removeListener('end', imapOnEnd);
reject(err);
}
...
function Error() { [native code] }
n/a