Initial backup 2026-02-17

This commit is contained in:
Krilly
2026-02-17 15:50:53 +00:00
commit 8902a93add
941 changed files with 131420 additions and 0 deletions

View File

@@ -0,0 +1,20 @@
# EditorConfig is awesome: http://EditorConfig.org
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
# 4 space indentation
[{*.js,*.json,.jshint*,.jscsrc}]
indent_style = space
indent_size = 4
# 2 space indentation for package.json
[package.json]
indent_size = 2
indent_style = space

View File

@@ -0,0 +1,61 @@
{
"requireCurlyBraces": [
"if",
"else",
"for",
"while",
"do",
"try",
"catch",
"default"
],
"requireSpaceAfterKeywords": [
"do",
"for",
"if",
"else",
"switch",
"case",
"try",
"void",
"while",
"return",
"function"
],
"disallowKeywords": ["with"],
"requireSpaceBeforeBlockStatements": true,
"requireSpacesInConditionalExpression": true,
"disallowSpacesInNamedFunctionExpression": {
"beforeOpeningRoundBrace": true
},
"disallowSpacesInFunctionDeclaration": {
"beforeOpeningRoundBrace": true
},
"requireSpacesInFunction": {
"beforeOpeningCurlyBrace": true
},
"disallowMultipleVarDecl": true,
"disallowMultipleLineBreaks": true,
"requireBlocksOnNewline": 1,
"requireSpacesInsideObjectBrackets": "allButNested",
"disallowQuotedKeysInObjects": "allButReserved",
"disallowSpaceAfterObjectKeys": true,
"requireSpaceBeforeObjectValues": true,
"requireCommaBeforeLineBreak": true,
"disallowSpaceBeforePostfixUnaryOperators": true,
"requireSpaceBeforeBinaryOperators": true,
"requireSpaceAfterBinaryOperators": true,
"disallowMixedSpacesAndTabs": true,
"disallowTrailingWhitespace": true,
"disallowTrailingComma": true,
"requireLineFeedAtFileEnd": true,
"requireCapitalizedConstructors": true,
"disallowYodaConditions": true,
"requireSpaceAfterLineComment": true,
"validateParameterSeparator": ", ",
"validateIndentation": 4,
"excludeFiles": [
"node_modules/**",
"coverage/**"
]
}

View File

@@ -0,0 +1,2 @@
node_modules
coverage

View File

@@ -0,0 +1,13 @@
{
"node": true,
"latedef": false,
"undef": true,
"unused": true,
"strict": true,
"mocha": true,
"globals": {
"Promise": true,
"xit": true,
"xdescribe": true
}
}

View File

@@ -0,0 +1,219 @@
# Change Log
All notable changes to this project will be documented in this file.
## 5.1.0 - 2021-06-01
#### Added
- \#89 - @mgkha
- Added wrapper for node-imap's `removeMessageLabel`
## 5.0.0 - 2020-03-30
#### Added
- \#40 - @brbeaird
- Added wrapper for node-imap's `closeBox` with support for autoExpunge
- This change makes use of default parameters in javascript, which was first
supported in Node.js v6. Previously this library did not explicitly specify
what Node.js versions it supported, so using this opportunity to specify that
and bump major version to ensure it does not inadvertently break people.
- \#60 - @synox
- Added `delete` which allows for deleting messages by uid.
## 4.3.0 - 2019-01-21
#### Added
- \#53 - @u2ros
- Added support for `UUENCODE` encoded attachment part decoding.
## 4.2.0 - 2018-11-08
#### Added
- \#50 - @iaarnio
- Added `ImapSimple.prototype.addBox()` and `ImapSimple.prototype.delBox()`
as wrappers around the same-named functions in the underlying node-imap
library.
## 4.1.0 - 2018-05-31
#### Added
- \#47 - @AurisAudentis
- Added `ImapSimple.prototype.getBoxes()` as a wrapper around the same-named
function in the underlying node-imap library.
## 4.0.0 - 2018-01-09
Between v3.1.0 and v3.2.0 #29 was merged to remove the `es6-promise` library from
this package's dependencies, but was never released as it was a semver major change.
Later, #41 was merged to add a new feature. #29 had been forgotten about, and
v3.2.0 (a semver-minor release) was issued for the library.
Because v3.2.0 contained breaking changes for users of the library on versions
of Node that don't include Promise support, we marked it as deprecated on the
npm registry and are issuing this 4.0.0 release as the current recommended
version.
Sorry :(
## 3.2.0 - 2017-08-21
#### Added
- \#41 - @jhannes
- Added wrapper function `append` on the connection object to append a message
to a mailbox.
## 3.1.0 - 2016-11-15
#### Added
- \#19 - @redpandatronicsuk
- Added wrapper functions to add and delete flags from messages.
- Added event listeners and corresponding options for listening for receiving
new mails, message updates (such as flag changes) and external message delete
events.
- Added `seqno` property to retrieved messages, so the message can be
correlated to received events.
## 3.0.0 - 2016-10-26
#### Fixed
- The ConnectionTimeoutError previously had its name set to 'BaseUrlNotSetError'.
This version fixes that, but since the error was part of the library's public API
and the name is technically something people could code against, the version has
received a major bump.
## 2.0.0 - 2016-09-28
Updated dependencies.
#### Changed
- The `es6-promise` module has changed its scheduler from `setImmediate()` to
`nextTick()` on Node 0.10. This directly affects this module's promise API,
so the major version has been bumped to indicate this. See
[the es6-promise changelog](https://github.com/stefanpenner/es6-promise/blob/master/CHANGELOG.md#300)
for more details about the change.
## 1.6.3 - 2016-07-20
#### Fixed
- \#15 - @johnkawakami - Parts of an email with 'BINARY' encoding will now be
decoded as such.
## 1.6.2 - 2016-05-17
#### Fixed
- \#11 - @nytr0gen - Library will now reject properly when a close or end event
is received when trying to connect.
## 1.6.1 - 2016-04-25
#### Fixed
- \#10 - @tuomastanner - fixed issue with decoding utf8 parts, specifically with
respect to interacting with gmail.
## 1.6.0 - 2016-03-11
#### Added
- \#9 - @bvschwartz - `getPartData` is now using [iconv-lite][iconv-lite] to automatically
decode message parts with an '8BIT' encoding, with a default 'utf-8' encoding set.
[iconv-lite]: https://github.com/ashtuchkin/iconv-lite
## 1.5.2 - 2016-02-04
#### Fixed
- \#7 - @srinath-imaginea - `fetchOptions` is now properly passed when using the callback
api of `search()`
## 1.5.1 - 2015-12-04
#### Fixed
- \#5 - @jbilcke - fixed incompatible use of all upper-case encoding name, instead of treating
the encoding as case-insensitive.
## 1.5.0 - 2015-05-22
#### Added
- added `addMessageLabel` and `moveMessage` wrapper methods to ImapSimple class
## 1.4.0 - 2015-05-22
#### Added
- added `getParts` to module export and `getPartData` to ImapSimple class
#### Fixed
- fixed strange bug where header was sometimes not being parsed
## 1.3.2 - 2015-03-06
#### Fixed
- fixed property used to determine whether an error was an authTimeout
## 1.3.1 - 2015-03-04
#### Fixed
- fixed `connect()` option `imap.authTimeout` default not being properly set.
## 1.3.0 - 2015-03-04
#### Removed
- removed `options.connectTimeout`. Support has remained for backwards
compatibility, but the recommended option for setting a connection timeout
moving forward is `options.imap.authTimeout`. Support for
`options.connectTimeout` will be removed on the next major release.
## 1.2.0 - 2015-03-02
#### Added
- 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()
## 1.1.0 - 2015-02-27
#### Added
- added .end() method to `ImapSimple` for disconnecting from imap server
## 1.0.0 - 2015-02-27
#### Added
- Initial commit.
For more information about keeping a changelog, check out [keepachangelog.com/](http://keepachangelog.com/)

View File

@@ -0,0 +1,30 @@
# Contributing
This project is **OPEN** open source
### What?
Individuals making significant and valuable contributions are given commit-access to the project to contribute as they
see fit.
### Rules
There are a few basic ground-rules for contributors:
1. **No `--force` pushes** or modifying the git history in any way.
1. **Non-master branches** ought to be used for ongoing work.
1. **External API changes and significant modifications** ought to be subject to an **internal pull-request** to solicit
feedback from other contributors.
1. Internal pull-requests to solicit feedback are *encouraged* for any other non-trivial contribution but left to the
discretion of the contributor.
1. Contributors should attempt to adhere to the prevailing code-style.
### Releases
Declaring formal releases remains the prerogative of the project maintainer(s).
### Changes to this arrangement
This is an experiment and feedback is welcome! This document may also be subject to pull-requests or changes by contributors where you believe you have something valuable to add or change.
*OPEN open source inspired by the [level](https://github.com/Level/community/blob/master/CONTRIBUTING.md) community*

View File

@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2018, Chad McElligott.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,434 @@
# imap-simple
**This library is no longer maintained and has been archived.**
A library providing a simpler interface for common use cases of [node-imap][], a robust imap client for node.js.
**Warning**: This library is missing a great deal of functionality from node-imap. If you have functionality you would
like to see, we're accepting pull requests!
### Examples
#### Retrieve the subject lines of all unread email
```js
var imaps = require('imap-simple');
var config = {
imap: {
user: 'your@email.address',
password: 'yourpassword',
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 = {
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);
// =>
// [ 'Hey Chad, long time no see!',
// 'Your amazon.com monthly statement',
// 'Hacker Newsletter Issue #445' ]
});
});
});
```
#### Retrieve Body Content
```js
var imaps = require('imap-simple');
const _ = require('lodash');
var config = {
imap: {
user: 'your@email.address',
password: 'yourpassword',
host: 'imap.gmail.com',
port: 993,
tls: true,
authTimeout: 3000
}
};
imaps.connect(config).then(function (connection) {
return connection.openBox('INBOX').then(function () {
var searchCriteria = ['1:5'];
var fetchOptions = {
bodies: ['HEADER', 'TEXT'],
};
return connection.search(searchCriteria, fetchOptions).then(function (messages) {
messages.forEach(function (item) {
var all = _.find(item.parts, { "which": "TEXT" })
var html = (Buffer.from(all.body, 'base64').toString('ascii'));
console.log(html)
});
});
});
});
```
#### Usage of Mailparser in combination with imap-simple
```js
var imaps = require('imap-simple');
const simpleParser = require('mailparser').simpleParser;
const _ = require('lodash');
var config = {
imap: {
user: 'your@email.address',
password: 'yourpassword',
host: 'imap.gmail.com',
port: 993,
tls: true,
authTimeout: 3000
}
};
imaps.connect(config).then(function (connection) {
return connection.openBox('INBOX').then(function () {
var searchCriteria = ['1:5'];
var fetchOptions = {
bodies: ['HEADER', 'TEXT', ''],
};
return connection.search(searchCriteria, fetchOptions).then(function (messages) {
messages.forEach(function (item) {
var all = _.find(item.parts, { "which": "" })
var id = item.attributes.uid;
var idHeader = "Imap-Id: "+id+"\r\n";
simpleParser(idHeader+all.body, (err, mail) => {
// access to the whole mail object
console.log(mail.subject)
console.log(mail.html)
});
});
});
});
});
```
#### Download all attachments from all unread email since yesterday
```js
var imaps = require('imap-simple');
var config = {
imap: {
user: 'your@email.address',
password: 'yourpassword',
host: 'imap.gmail.com',
port: 993,
tls: true,
authTimeout: 3000
}
};
imaps.connect(config).then(function (connection) {
connection.openBox('INBOX').then(function () {
// Fetch emails from the last 24h
var delay = 24 * 3600 * 1000;
var yesterday = new Date();
yesterday.setTime(Date.now() - delay);
yesterday = yesterday.toISOString();
var searchCriteria = ['UNSEEN', ['SINCE', yesterday]];
var fetchOptions = { bodies: ['HEADER.FIELDS (FROM TO SUBJECT DATE)'], struct: true };
// 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.toUpperCase() === '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
};
});
}));
});
return Promise.all(attachments);
}).then(function (attachments) {
console.log(attachments);
// =>
// [ { filename: 'cats.jpg', data: Buffer() },
// { filename: 'pay-stub.pdf', data: Buffer() } ]
});
});
```
### Append a message to your drafts folder
```js
var imaps = require('imap-simple');
var config = {
imap: {
user: 'your@email.address',
password: 'yourpassword',
host: 'imap.gmail.com',
port: 993,
tls: true,
authTimeout: 3000
}
};
imaps.connect(config).then(function (connection) {
const message = `Content-Type: text/plain
To: jhannes@gmail.com
Subject: Hello world
Hi
This is a test message
`;
connection.append(message.toString(), {mailbox: 'Drafts', flags: '\\Draft'});
});
```
### Open messages and delete them
```js
imaps.connect(config).then(function (connection) {
connection.openBox('INBOX').then(function () {
var searchCriteria = ['ALL'];
var fetchOptions = { bodies: ['TEXT'], struct: true };
return connection.search(searchCriteria, fetchOptions);
//Loop over each message
}).then(function (messages) {
let taskList = messages.map(function (message) {
return new Promise((res, rej) => {
var parts = imaps.getParts(message.attributes.struct);
parts.map(function (part) {
return connection.getPartData(message, part)
.then(function (partData) {
//Display e-mail body
if (part.disposition == null && part.encoding != "base64"){
console.log(partData);
}
//Mark message for deletion
connection.addFlags(message.attributes.uid, "\Deleted", (err) => {
if (err){
console.log('Problem marking message for deletion');
rej(err);
}
res(); //Final resolve
})
});
});
});
})
return Promise.all(taskList).then(() => {
connection.imap.closeBox(true, (err) => { //Pass in false to avoid delete-flagged messages being removed
if (err){
console.log(err);
}
});
connection.end();
});
});
});
```
### delete messages by uid
```js
imaps.connect(config).then(connection => {
return connection.openBox('INBOX')
.then(() => connection.search(['ALL'], {bodies: ['HEADER']}))
.then( messages => {
// select messages from bob
const uidsToDelete = messages
.filter( message => {
return message.parts
.filter( part => part.which === 'HEADER')[0].body.to[0] === 'bob@example.com';
})
.map(message => message.attributes.uid);
return connection.deleteMessage(uidsToDelete);
});
});
```
## API
### Exported module
- **connect**(<*object*> options, [<*function*> callback]) - *Promise* - Main entry point. Connect to an Imap server.
Upon successfully connecting to the Imap server, either calls the provided callback with signature `(err, connection)`,
or resolves the returned promise with `connection`, where `connection` is an instance of *ImapSimple*. If the connection
times out, either the callback will be called with the `err` property set to an instance of *ConnectionTimeoutError*, or
the returned promise will be rejected with the same. Valid `options` properties are:
- **imap**: Options to pass to node-imap constructor 1:1
- **connectTimeout**: Time in milliseconds to wait before giving up on a connection attempt. *(Deprecated: please
use `options.imap.authTimeout` instead)*
- **errors.ConnectionTimeoutError**(<*number*> timeout) - *ConnectionTimeoutError* - Error thrown when a connection
attempt has timed out.
- **getParts**(<*Array*> struct) - *Array* - 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.
- **ImapSimple**(<*object*> imap) - *ImapSimple* - constructor for creating an instance of ImapSimple. Mostly used for
testing.
### ImapSimple class
- **addFlags**(<*mixed*> uid, <*string*> flag, [<*function*> callback]) - *Promise* - Adds the provided
flag(s) to the specified message(s). `uid` is the *uid* of the message you want to add the flag to or an array of
*uids*. `flag` is either a string or array of strings indicating the flags to add. When completed, either calls
the provided callback with signature `(err)`, or resolves the returned promise.
- **addMessageLabel**(<*mixed*> source, <*mixed*> label, [<*function*> callback]) - *Promise* - Adds the provided
label(s) to the specified message(s). `source` corresponds to a node-imap *MessageSource* which specifies the messages
to be moved. `label` is either a string or array of strings indicating the labels to add. When completed, either calls
the provided callback with signature `(err)`, or resolves the returned promise.
- **removeMessageLabel**(<*mixed*> source, <*mixed*> label, [<*function*> callback]) - *Promise* - Removes the provided
label(s) from the specified message(s). `source` corresponds to a node-imap *MessageSource* which specifies the messages
to be removed. `label` is either a string or array of strings indicating the labels to remove. When completed, either calls
the provided callback with signature `(err)`, or resolves the returned promise.
- **append**(<*mixed*> message, [<*object*> options], [<*function*> callback]) - *Promise* - Appends the argument
message to the currently open mailbox or another mailbox. `message` is a RFC-822 compatible MIME message. Valid `options`
are *mailbox*, *flags* and *date*. When completed, either calls the provided callback with signature `(err)`, or resolves
the returned promise.
- **delFlags**(<*mixed*> uid, <*string*> flag, [<*function*> callback]) - *Promise* - Removes the provided
flag(s) from the specified message(s). `uid` is the *uid* of the message you want to remove the flag from or an array of
*uids*. `flag` is either a string or array of strings indicating the flags to remove. When completed, either calls
the provided callback with signature `(err)`, or resolves the returned promise.
- **end**() - *undefined* - Close the connection to the imap server.
- **getBoxes**([<*function*> callback]) - *Promise* - Returns the full list of mailboxes (folders). Upon success, either
the provided callback will be called with signature `(err, boxes)`, or the returned promise will be resolved with `boxes`.
`boxes` is the exact object returned from the node-imap *getBoxes()* result.
- **getPartData**(<*object*> message, <*object*> part, [<*function*> callback]) - *Promise* - Downloads part data
(which is either part of the message body, or an attachment). Upon success, either the provided callback will be called
with signature `(err, data)`, or the returned promise will be resolved with `data`. The data will be automatically
decoded based on its encoding. If the encoding of the part is not supported, an error will occur.
- **deleteMessage**(<*mixed*> uid, [<*function*> callback]) - *Promise* - Deletes the specified
message(s). `uid` is the *uid* of the message you want to add the flag to or an array of *uids*.
When completed, either calls the provided callback with signature `(err)`, or resolves the returned promise.
- **moveMessage**(<*mixed*> source, <*string*> boxName, [<*function*> callback]) - *Promise* - Moves the specified
message(s) in the currently open mailbox to another mailbox. `source` corresponds to a node-imap *MessageSource* which
specifies the messages to be moved. When completed, either calls the provided callback with signature `(err)`, or
resolves the returned promise.
- **openBox**(<*string*> boxName, [<*function*> callback]) - *Promise* - Open a mailbox, calling the provided callback
with signature `(err, boxName)`, or resolves the returned promise with `boxName`.
- **closeBox**(<*boolean*> [autoExpunge = true], [<*function*> callback]) - *Promise* - Close a mailbox, calling the provided callback
with signature `(err)`, or resolves the returned promise. If autoExpunge is true, any messages marked as Deleted in the currently
open mailbox will be removed.
- **addBox**(<*string*> boxName, [<*function*> callback]) - *Promise* - Create a mailbox, calling the provided callback
with signature `(err, boxName)`, or resolves the returned promise with `boxName`.
- **delBox**(<*string*> boxName, [<*function*> callback]) - *Promise* - Delete a mailbox, calling the provided callback
with signature `(err, boxName)`, or resolves the returned promise with `boxName`.
- **search**(<*object*> searchCriteria, [<*object*> fetchOptions], [<*function*> callback]) - *Promise* - Search for and
retrieve mail in the currently open mailbox. The search is performed based on the provided `searchCriteria`, which is
the exact same format as [node-imap][] requires. All results will be subsequently downloaded, according to the options
provided by `fetchOptions`, which are also identical to those passed to `fetch` of [node-imap][]. Upon a successful
search+fetch operation, either the provided callback will be called with signature `(err, results)`, or the returned
promise will be resolved with `results`. The format of `results` is detailed below. 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.
```js
// [{
// attributes: object,
// parts: [ { which: string, size: number, body: string }, ... ]
// }, ...]
```
## Server events
Functions to listen to server events are configured in the configuration object that is passed to the `connect` function.
ImapSimple only implements a subset of the server event functions that *node-imap* supports, [see here](https://github.com/mscdex/node-imap#connection-events),
which are `mail`, `expunge` and `update`. Add them to the configuration object as follows:
```
var config = {
imap: {
...
},
onmail: function (numNewMail) {
...
},
onexpunge: function (seqno) {
...
},
onupdate: function (seqno, info) {
...
}
};
```
For more information [see here](https://github.com/mscdex/node-imap#connection-events).
## Contributing
Pull requests welcome! This project really needs tests, so those would be very welcome. If you have a use case you want
supported, please feel free to add, but be sure to follow the patterns established thus far, mostly:
- support promises **AND** callbacks
- make your api as simple as possible
- don't worry about exposing implementation details of [node-imap][] when needed
This project is **OPEN** open source. See [CONTRIBUTING.md](CONTRIBUTING.md) for more details about contributing.
## Semver
This project follows [semver](http://semver.org/). Namely:
- new MAJOR versions when incompatible API changes are made,
- new MINOR versions for backwards-compatible feature additions,
- new PATCH versions for backwards-compatible bug fixes
## License
[MIT](LICENSE-MIT)
[node-imap]: https://github.com/mscdex/node-imap

View File

@@ -0,0 +1,3 @@
'use strict';
module.exports = require('./lib/imapSimple');

View File

@@ -0,0 +1,24 @@
'use strict';
var util = require('util');
/**
* Error thrown when a connection attempt has timed out
*
* @param {number} timeout timeout in milliseconds that the connection waited before timing out
* @constructor
*/
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';
}
util.inherits(ConnectionTimeoutError, Error);
exports.ConnectionTimeoutError = ConnectionTimeoutError;

View File

@@ -0,0 +1,65 @@
'use strict';
var Imap = require('imap');
/**
* Given an 'ImapMessage' from the node-imap library,
* retrieves the message formatted as:
*
* {
* attributes: object,
* parts: [ { which: string, size: number, body: string }, ... ]
* }
*
* @param {object} message an ImapMessage from the node-imap library
* @returns {Promise} a promise resolving to `message` with schema as described above
*/
module.exports = function getMessage(message) {
return new Promise(function (resolve) {
var attributes;
var messageParts = [];
var isHeader = /^HEADER/g;
function messageOnBody(stream, info) {
var body = '';
function streamOnData(chunk) {
body += chunk.toString('utf8');
}
stream.on('data', streamOnData);
stream.once('end', function streamOnEnd() {
stream.removeListener('data', streamOnData);
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) {
attributes = attrs;
}
function messageOnEnd() {
message.removeListener('body', messageOnBody);
message.removeListener('attributes', messageOnAttributes);
resolve({
attributes: attributes,
parts: messageParts
});
}
message.on('body', messageOnBody);
message.once('attributes', messageOnAttributes);
message.once('end', messageOnEnd);
});
};

View File

@@ -0,0 +1,715 @@
'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
};

View File

@@ -0,0 +1,57 @@
{
"name": "imap-simple",
"version": "5.1.0",
"description": "Wrapper over node-imap, providing a simpler api for common use cases",
"main": "index.js",
"scripts": {
"test": "mocha",
"lint": "jscs . && jshint .",
"cover": "istanbul cover --report html _mocha"
},
"keywords": [
"imap",
"node-imap"
],
"contributors": [
"Aravindo Wingeier <synox.github@dubgo.com>",
"Brian Beaird",
"Bruce V. Schwartz <bruce@z2a.org>",
"Chad McElligott <chad.mcelligott@gmail.com>",
"Dominik Beste <dominik.beste@gmail.com>",
"Erik Bernhardsson <erikbern@spotify.com>",
"Ilari Aarnio",
"Johannes Brodwall <johannes@brodwall.com>",
"John Kawakami <jkawakami@ebullient.net>",
"Julian Bilcke <julian.bilcke@gmail.com>",
"Maxiem <maxiemgeldhof@msn.com>",
"Nate Watson <watsonn2013@my.fit.edu>",
"Robert Vulpe <nytr0gen.george@gmail.com>",
"Tuomas Tanner",
"u2ros <urbernik@gmail.com>",
"Kaung Htet Aung <kaung.h.ag@gmail.com>"
],
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/chadxz/imap-simple.git"
},
"engines": {
"node": ">=6"
},
"dependencies": {
"iconv-lite": "~0.4.13",
"imap": "^0.8.18",
"nodeify": "^1.0.0",
"quoted-printable": "^1.0.0",
"utf8": "^2.1.1",
"uuencode": "0.0.4"
},
"devDependencies": {
"hoodiecrow-imap": "^2.1.0",
"chai": "^3.5.0",
"istanbul": "^0.4.5",
"jscs": "^3.0.7",
"jshint": "^2.9.3",
"mocha": "^3.1.0"
}
}

View File

@@ -0,0 +1,35 @@
'use strict';
var hoodiecrow = require("hoodiecrow-imap");
function startTestServer(port=1143, debug=false) {
var server = hoodiecrow({
plugins: ["ID", "STARTTLS" /*, "LOGINDISABLED"*/ , "SASL-IR", "AUTH-PLAIN", "NAMESPACE", "IDLE", "ENABLE", "CONDSTORE", "XTOYBIRD", "LITERALPLUS", "UNSELECT", "SPECIAL-USE", "CREATE-SPECIAL-USE"],
id: {
name: "hoodiecrow",
version: "0.1"
},
storage: {
INBOX: {}
},
debug: debug
});
return new Promise(function (resolve, reject) {
server.listen(port, function () {
resolve(server);
});
});
}
function appendMessage(connection, to, subject, flags = '') {
var message = `Content-Type: text/plain
To: ${to}
Subject: ${subject}
This is a test message`;
connection.append(message, { mailbox: 'INBOX', flags: flags });
}
module.exports = { startTestServer, appendMessage };

View File

@@ -0,0 +1,111 @@
'use strict';
var {startTestServer, appendMessage} = require('./imapTestServer');
var expect = require('chai').expect;
var serverInstance = null;
beforeEach(function () {
return startTestServer()
.then(function (server) {
serverInstance = server;
});
});
afterEach(function () {
serverInstance.close();
});
var config = {
imap: {
user: 'testuser',
password: 'testpass',
host: 'localhost',
port: 1143,
tls: false,
authTimeout: 3000
}
};
describe('imap-simple', function () {
this.timeout(20000);
var imaps = require('../');
it('lists unseen emails only', function () {
return imaps.connect(config).then(function (connection) {
return connection.openBox('INBOX')
.then(function () {return appendMessage(connection, 'jim@example.com', 'unseen 1');})
.then(function () {return appendMessage(connection, 'john@example.com', 'seen 2', '\\Seen');})
.then(function () {return appendMessage(connection, 'james@example.com', 'unseen 3');})
.then(function () {
var searchCriteria = [
'UNSEEN'
];
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];
});
expect(subjects).to.eql([
'unseen 1',
'unseen 3'
]);
console.log(subjects);
});
});
});
});
it('deletes messages', function () {
return imaps.connect(config).then(function (connection) {
return connection.openBox('INBOX')
.then(function () {return appendMessage(connection, 'jim@example.com', 'hello from jim');})
.then(function () {return appendMessage(connection, 'bob@example.com', 'hello from bob');})
.then(function () {return appendMessage(connection, 'bob@example.com', 'hello again from bob');})
.then(function () {return connection.search(['ALL'], {bodies: ['HEADER']});})
.then(function (messages) {
var uidsToDelete = messages
.filter(function (message) {
return message.parts.filter(function (part) {
return part.which === 'HEADER';
})[0].body.to[0] === 'bob@example.com';
})
.map(function (message) {
return message.attributes.uid;
});
return connection.deleteMessage(uidsToDelete);
})
.then(function () {
return connection.search(['ALL'], {bodies: ['HEADER']});
}).then(function (messages) {
var subjects = messages.map(function (res) {
return res.parts.filter(function (part) {
return part.which === 'HEADER';
})[0].body.subject[0];
});
expect(subjects).to.eql([
'hello from jim'
]);
console.log(subjects);
});
});
});
});