716 lines
22 KiB
JavaScript
716 lines
22 KiB
JavaScript
'use strict';
|
|
var Imap = require('imap');
|
|
var nodeify = require('nodeify');
|
|
var getMessage = require('./helpers/getMessage');
|
|
var errors = require('./errors');
|
|
var util = require('util');
|
|
var EventEmitter = require('events').EventEmitter;
|
|
var qp = require('quoted-printable');
|
|
var iconvlite = require('iconv-lite');
|
|
var utf8 = require('utf8');
|
|
var uuencode = require('uuencode');
|
|
|
|
/**
|
|
* Constructs an instance of ImapSimple
|
|
*
|
|
* @param {object} imap a constructed node-imap connection
|
|
* @constructor
|
|
* @class ImapSimple
|
|
*/
|
|
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);
|
|
});
|
|
}
|
|
|
|
util.inherits(ImapSimple, EventEmitter);
|
|
|
|
/**
|
|
* disconnect from the imap server
|
|
*/
|
|
ImapSimple.prototype.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();
|
|
};
|
|
|
|
/**
|
|
* Open a mailbox
|
|
*
|
|
* @param {string} boxName The name of the box to open
|
|
* @param {function} [callback] Optional callback, receiving signature (err, boxName)
|
|
* @returns {undefined|Promise} Returns a promise when no callback is specified, resolving to `boxName`
|
|
* @memberof ImapSimple
|
|
*/
|
|
ImapSimple.prototype.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);
|
|
});
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Close a mailbox
|
|
*
|
|
* @param {boolean} [autoExpunge=true] If autoExpunge is true, any messages marked as Deleted in the currently open mailbox will be remove
|
|
* @param {function} [callback] Optional callback, receiving signature (err)
|
|
* @returns {undefined|Promise} Returns a promise when no callback is specified, resolving to `boxName`
|
|
* @memberof ImapSimple
|
|
*/
|
|
ImapSimple.prototype.closeBox = function (autoExpunge=true, callback) {
|
|
var self = this;
|
|
|
|
if (typeof(autoExpunge) == 'function'){
|
|
callback = autoExpunge;
|
|
autoExpunge = true;
|
|
}
|
|
|
|
if (callback) {
|
|
return nodeify(this.closeBox(autoExpunge), callback);
|
|
}
|
|
|
|
return new Promise(function (resolve, reject) {
|
|
|
|
self.imap.closeBox(autoExpunge, function (err, result) {
|
|
|
|
if (err) {
|
|
reject(err);
|
|
return;
|
|
}
|
|
|
|
resolve(result);
|
|
});
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Search the currently open mailbox, and retrieve the results
|
|
*
|
|
* Results are in the form:
|
|
*
|
|
* [{
|
|
* attributes: object,
|
|
* parts: [ { which: string, size: number, body: string }, ... ]
|
|
* }, ...]
|
|
*
|
|
* See node-imap's ImapMessage signature for information about `attributes`, `which`, `size`, and `body`.
|
|
* For any message part that is a `HEADER`, the body is automatically parsed into an object.
|
|
*
|
|
* @param {object} searchCriteria Criteria to use to search. Passed to node-imap's .search() 1:1
|
|
* @param {object} fetchOptions Criteria to use to fetch the search results. Passed to node-imap's .fetch() 1:1
|
|
* @param {function} [callback] Optional callback, receiving signature (err, results)
|
|
* @returns {undefined|Promise} Returns a promise when no callback is specified, resolving to `results`
|
|
* @memberof ImapSimple
|
|
*/
|
|
ImapSimple.prototype.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);
|
|
});
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Download a "part" (either a portion of the message body, or an attachment)
|
|
*
|
|
* @param {object} message The message returned from `search()`
|
|
* @param {object} part The message part to be downloaded, from the `message.attributes.struct` Array
|
|
* @param {function} [callback] Optional callback, receiving signature (err, data)
|
|
* @returns {undefined|Promise} Returns a promise when no callback is specified, resolving to `data`
|
|
* @memberof ImapSimple
|
|
*/
|
|
ImapSimple.prototype.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 (encoding === 'UUENCODE') {
|
|
var parts = data.toString().split('\n'); // remove newline characters
|
|
var merged = parts.splice(1, parts.length - 4).join(''); // remove excess lines and join lines with empty string
|
|
resolve(uuencode.decode(merged));
|
|
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);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Moves the specified message(s) in the currently open mailbox to another mailbox.
|
|
*
|
|
* @param {string|Array} source The node-imap `MessageSource` indicating the message(s) from the current open mailbox
|
|
* to move.
|
|
* @param {string} boxName The mailbox to move the message(s) to.
|
|
* @param {function} [callback] Optional callback, receiving signature (err)
|
|
* @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;
|
|
}
|
|
|
|
resolve();
|
|
});
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Adds the provided label(s) to the specified message(s).
|
|
*
|
|
* This is a Gmail extension method (X-GM-EXT-1)
|
|
*
|
|
* @param {string|Array} source The node-imap `MessageSource` indicating the message(s) to add the label(s) to.
|
|
* @param {string|Array} labels Either a single string or an array of strings indicating the labels to add to the
|
|
* message(s).
|
|
* @param {function} [callback] Optional callback, receiving signature (err)
|
|
* @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;
|
|
}
|
|
|
|
resolve();
|
|
});
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Remove the provided label(s) from the specified message(s).
|
|
*
|
|
* This is a Gmail extension method (X-GM-EXT-1)
|
|
*
|
|
* @param {string|Array} source The node-imap `MessageSource` indicating the message(s) to remove the label(s) from.
|
|
* @param {string|Array} labels Either a single string or an array of strings indicating the labels to remove from the
|
|
* message(s).
|
|
* @param {function} [callback] Optional callback, receiving signature (err)
|
|
* @returns {undefined|Promise} Returns a promise when no callback is specified, resolving when the action succeeds.
|
|
* @memberof ImapSimple
|
|
*/
|
|
ImapSimple.prototype.removeMessageLabel = function (source, labels, callback) {
|
|
var self = this;
|
|
|
|
if (callback) {
|
|
return nodeify(self.removeMessageLabel(source, labels), callback);
|
|
}
|
|
|
|
return new Promise(function (resolve, reject) {
|
|
self.imap.delLabels(source, labels, function (err) {
|
|
if (err) {
|
|
reject(err);
|
|
return;
|
|
}
|
|
|
|
resolve();
|
|
});
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Adds the provided flag(s) to the specified message(s).
|
|
*
|
|
* @param {string|Array} uid The messages uid
|
|
* @param {string|Array} flags Either a single string or an array of strings indicating the flags to add to the
|
|
* message(s).
|
|
* @param {function} [callback] Optional callback, receiving signature (err)
|
|
* @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;
|
|
}
|
|
|
|
resolve();
|
|
});
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Removes the provided flag(s) to the specified message(s).
|
|
*
|
|
* @param {string|Array} uid The messages uid
|
|
* @param {string|Array} flags Either a single string or an array of strings indicating the flags to remove from the
|
|
* message(s).
|
|
* @param {function} [callback] Optional callback, receiving signature (err)
|
|
* @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;
|
|
}
|
|
|
|
resolve();
|
|
});
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Deletes the specified message(s).
|
|
*
|
|
* @param {string|Array} uid The uid or array of uids indicating the messages to be deleted
|
|
* @param {function} [callback] Optional callback, receiving signature (err)
|
|
* @returns {undefined|Promise} Returns a promise when no callback is specified, resolving when the action succeeds.
|
|
* @memberof ImapSimple
|
|
*/
|
|
ImapSimple.prototype.deleteMessage = function (uid, callback) {
|
|
var self = this;
|
|
|
|
if (callback) {
|
|
return nodeify(self.deleteMessage(uid), callback);
|
|
}
|
|
|
|
return new Promise(function (resolve, reject) {
|
|
self.imap.addFlags(uid, '\\Deleted', function (err) {
|
|
if (err) {
|
|
reject(err);
|
|
return;
|
|
}
|
|
self.imap.expunge( function (err) {
|
|
if (err) {
|
|
reject(err);
|
|
return;
|
|
}
|
|
resolve();
|
|
});
|
|
});
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Appends a mime-encoded message to a mailbox
|
|
*
|
|
* @param {string|Buffer} message The messages to append to the mailbox
|
|
* @param {object} [options]
|
|
* @param {string} [options.mailbox] The mailbox to append the message to.
|
|
Defaults to the currently open mailbox.
|
|
* @param {string|Array<String>} [options.flag] A single flag (e.g. 'Seen') or an array
|
|
of flags (e.g. ['Seen', 'Flagged']) to append to the message. Defaults to
|
|
no flags.
|
|
* @param {function} [callback] Optional callback, receiving signature (err)
|
|
* @returns {undefined|Promise} Returns a promise when no callback is specified, resolving when the action succeeds.
|
|
* @memberof ImapSimple
|
|
*/
|
|
ImapSimple.prototype.append = function (message, options, callback) {
|
|
var self = this;
|
|
|
|
if (callback) {
|
|
return nodeify(self.append(message, options), callback);
|
|
}
|
|
|
|
return new Promise(function (resolve, reject) {
|
|
self.imap.append(message, options, function (err) {
|
|
if (err) {
|
|
reject(err);
|
|
return;
|
|
}
|
|
|
|
resolve();
|
|
});
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Returns a list of mailboxes (folders).
|
|
*
|
|
* @param {function} [callback] Optional callback containing 'boxes' object.
|
|
* @returns {undefined|Promise} Returns a promise when no callback is specified,
|
|
* resolving when the action succeeds.
|
|
*/
|
|
|
|
ImapSimple.prototype.getBoxes = function (callback) {
|
|
var self = this;
|
|
|
|
if (callback) {
|
|
return nodeify(self.getBoxes(), callback);
|
|
}
|
|
|
|
return new Promise(function (resolve, reject) {
|
|
self.imap.getBoxes(function (err, boxes) {
|
|
if (err) {
|
|
reject(err);
|
|
return;
|
|
}
|
|
|
|
resolve(boxes);
|
|
});
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Add new mailbox (folder)
|
|
*
|
|
* @param {string} boxName The name of the box to added
|
|
* @param {function} [callback] Optional callback, receiving signature (err, boxName)
|
|
* @returns {undefined|Promise} Returns a promise when no callback is specified, resolving to `boxName`
|
|
* @memberof ImapSimple
|
|
*/
|
|
ImapSimple.prototype.addBox = function (boxName, callback) {
|
|
var self = this;
|
|
|
|
if (callback) {
|
|
return nodeify(this.addBox(boxName), callback);
|
|
}
|
|
|
|
return new Promise(function (resolve, reject) {
|
|
|
|
self.imap.addBox(boxName, function (err) {
|
|
|
|
if (err) {
|
|
reject(err);
|
|
return;
|
|
}
|
|
|
|
resolve(boxName);
|
|
});
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Delete mailbox (folder)
|
|
*
|
|
* @param {string} boxName The name of the box to deleted
|
|
* @param {function} [callback] Optional callback, receiving signature (err, boxName)
|
|
* @returns {undefined|Promise} Returns a promise when no callback is specified, resolving to `boxName`
|
|
* @memberof ImapSimple
|
|
*/
|
|
ImapSimple.prototype.delBox = function (boxName, callback) {
|
|
var self = this;
|
|
|
|
if (callback) {
|
|
return nodeify(this.delBox(boxName), callback);
|
|
}
|
|
|
|
return new Promise(function (resolve, reject) {
|
|
|
|
self.imap.delBox(boxName, function (err) {
|
|
|
|
if (err) {
|
|
reject(err);
|
|
return;
|
|
}
|
|
|
|
resolve(boxName);
|
|
});
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Connect to an Imap server, returning an ImapSimple instance, which is a wrapper over node-imap to
|
|
* simplify it's api for common use cases.
|
|
*
|
|
* @param {object} options
|
|
* @param {object} options.imap Options to pass to node-imap constructor 1:1
|
|
* @param {function} [callback] Optional callback, receiving signature (err, connection)
|
|
* @returns {undefined|Promise} Returns a promise when no callback is specified, resolving to `connection`
|
|
*/
|
|
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();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Given the `message.attributes.struct`, retrieve a flattened array of `parts` objects that describe the structure of
|
|
* the different parts of the message's body. Useful for getting a simple list to iterate for the purposes of,
|
|
* for example, finding all attachments.
|
|
*
|
|
* Code taken from http://stackoverflow.com/questions/25247207/how-to-read-and-save-attachments-using-node-imap
|
|
*
|
|
* @param {Array} struct The `message.attributes.struct` value from the message you wish to retrieve parts for.
|
|
* @param {Array} [parts] The list of parts to push to.
|
|
* @returns {Array} a flattened array of `parts` objects that describe the structure of the different parts of the
|
|
* message's body
|
|
*/
|
|
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;
|
|
}
|
|
|
|
module.exports = {
|
|
connect: connect,
|
|
ImapSimple: ImapSimple,
|
|
parseHeader: Imap.parseHeader,
|
|
getParts: getParts,
|
|
errors: errors
|
|
};
|