mirror of
https://github.com/mgerb/mywebsite
synced 2026-01-12 10:52:47 +00:00
554 lines
12 KiB
Markdown
554 lines
12 KiB
Markdown
# buildmail
|
|
|
|
Low level rfc2822 message composer that streams output. Define your own mime tree, no magic included.
|
|
|
|
Ported from [MailBuild](https://github.com/whiteout-io/mailbuild) of the [emailjs.org](http://emailjs.org/) project. This port uses similar API but is for Node only and streams the output.
|
|
|
|
## Usage
|
|
|
|
Install with npm
|
|
|
|
npm install buildmail
|
|
|
|
Require in your scripts
|
|
|
|
```javascript
|
|
var BuildMail = require('buildmail');
|
|
```
|
|
|
|
## API
|
|
|
|
Create a new `BuildMail` object with
|
|
|
|
```javascript
|
|
var builder = new BuildMail(contentType [, options]);
|
|
```
|
|
|
|
Where
|
|
|
|
* **contentType** - define the content type for created node. Can be left blank for attachments (content type derived from `filename` option if available)
|
|
* **options** - an optional options object
|
|
* **filename** - *String* filename for an attachment node
|
|
* **baseBoundary** - *String* shared part of the unique multipart boundary (generated randomly if not set)
|
|
* **keepBcc** - *Boolean* If true keep the Bcc value in generated headers (default is to remove it)
|
|
|
|
## Methods
|
|
|
|
The same methods apply to the root node created with `new BuildMail()` and to any child nodes.
|
|
|
|
### createChild
|
|
|
|
Creates and appends a child node to the node object
|
|
|
|
```javascript
|
|
node.createChild(contentType, options)
|
|
```
|
|
|
|
The same arguments apply as with `new BuildMail()`. Created node object is returned.
|
|
|
|
**Example**
|
|
|
|
```javascript
|
|
new BuildMail('multipart/mixed').
|
|
createChild('multipart/related').
|
|
createChild('text/plain');
|
|
```
|
|
|
|
Generates the following mime tree:
|
|
|
|
```
|
|
multipart/mixed
|
|
↳ multipart/related
|
|
↳ text/plain
|
|
```
|
|
|
|
### appendChild
|
|
|
|
Appends an existing child node to the node object. Removes the node from an existing tree if needed.
|
|
|
|
```javascript
|
|
node.appendChild(childNode)
|
|
```
|
|
|
|
Where
|
|
|
|
* **childNode** - child node to be appended
|
|
|
|
Method returns appended child node.
|
|
|
|
**Example**
|
|
|
|
```javascript
|
|
var childNode = new BuildMail('text/plain'),
|
|
rootNode = new BuildMail('multipart/mixed');
|
|
rootnode.appendChild(childNode);
|
|
```
|
|
|
|
Generates the following mime tree:
|
|
|
|
```
|
|
multipart/mixed
|
|
↳ text/plain
|
|
```
|
|
|
|
## replace
|
|
|
|
Replaces current node with another node
|
|
|
|
```javascript
|
|
node.replace(replacementNode)
|
|
```
|
|
|
|
Where
|
|
|
|
* **replacementNode** - node to replace the current node with
|
|
|
|
Method returns replacement node.
|
|
|
|
**Example**
|
|
|
|
```javascript
|
|
var rootNode = new BuildMail('multipart/mixed'),
|
|
childNode = rootNode.createChild('text/plain');
|
|
childNode.replace(new BuildMail('text/html'));
|
|
```
|
|
|
|
Generates the following mime tree:
|
|
|
|
```
|
|
multipart/mixed
|
|
↳ text/html
|
|
```
|
|
|
|
## remove
|
|
|
|
Removes current node from the mime tree. Does not make a lot of sense for a root node.
|
|
|
|
```javascript
|
|
node.remove();
|
|
```
|
|
|
|
Method returns removed node.
|
|
|
|
**Example**
|
|
|
|
```javascript
|
|
|
|
var rootNode = new BuildMail('multipart/mixed'),
|
|
childNode = rootNode.createChild('text/plain');
|
|
childNode.remove();
|
|
```
|
|
|
|
Generates the following mime tree:
|
|
|
|
```
|
|
multipart/mixed
|
|
```
|
|
|
|
## setHeader
|
|
|
|
Sets a header value. If the value for selected key exists, it is overwritten.
|
|
|
|
You can set multiple values as well by using `[{key:'', value:''}]` or
|
|
`{key: 'value'}` structures as the first argument.
|
|
|
|
```javascript
|
|
node.setHeader(key, value);
|
|
```
|
|
|
|
Where
|
|
|
|
* **key** - *String|Array|Object* Header key or a list of key value pairs
|
|
* **value** - *String* Header value
|
|
|
|
Method returns current node.
|
|
|
|
**Example**
|
|
|
|
```javascript
|
|
new BuildMail('text/plain').
|
|
setHeader('content-disposition', 'inline').
|
|
setHeader({
|
|
'content-transfer-encoding': '7bit'
|
|
}).
|
|
setHeader([
|
|
{key: 'message-id', value: 'abcde'}
|
|
```
|
|
|
|
Generates the following header:
|
|
|
|
```
|
|
Content-type: text/plain
|
|
Content-Disposition: inline
|
|
Content-Transfer-Encoding: 7bit
|
|
Message-Id: <abcde>
|
|
```
|
|
|
|
## addHeader
|
|
|
|
Adds a header value. If the value for selected key exists, the value is appended
|
|
as a new field and old one is not touched.
|
|
|
|
You can set multiple values as well by using `[{key:'', value:''}]` or
|
|
`{key: 'value'}` structures as the first argument.
|
|
|
|
```javascript
|
|
node.addHeader(key, value);
|
|
```
|
|
|
|
Where
|
|
|
|
* **key** - *String|Array|Object* Header key or a list of key value pairs
|
|
* **value** - *String* Header value
|
|
|
|
Method returns current node.
|
|
|
|
**Example**
|
|
|
|
```javascript
|
|
new BuildMail('text/plain').
|
|
addHeader('X-Spam', '1').
|
|
setHeader({
|
|
'x-spam': '2'
|
|
}).
|
|
setHeader([
|
|
{key: 'x-spam', value: '3'}
|
|
]);
|
|
```
|
|
|
|
Generates the following header:
|
|
|
|
```
|
|
Content-type: text/plain
|
|
X-Spam: 1
|
|
X-Spam: 2
|
|
X-Spam: 3
|
|
```
|
|
|
|
## getHeader
|
|
|
|
Retrieves the first mathcing value of a selected key
|
|
|
|
```javascript
|
|
node.getHeader(key)
|
|
```
|
|
|
|
Where
|
|
|
|
* **key** - *String* Key to search for
|
|
|
|
**Example**
|
|
|
|
```javascript
|
|
new BuildMail('text/plain').getHeader('content-type'); // text/plain
|
|
```
|
|
|
|
## buildHeaders
|
|
|
|
Builds the current header info into a header block that can be used in an e-mail
|
|
|
|
```javascript
|
|
var headers = node.buildHeaders()
|
|
```
|
|
|
|
**Example**
|
|
|
|
```javascript
|
|
new BuildMail('text/plain').
|
|
addHeader('X-Spam', '1').
|
|
setHeader({
|
|
'x-spam': '2'
|
|
}).
|
|
setHeader([
|
|
{key: 'x-spam', value: '3'}
|
|
]).buildHeaders();
|
|
```
|
|
|
|
returns the following String
|
|
|
|
```
|
|
Content-Type: text/plain
|
|
X-Spam: 3
|
|
Date: Sat, 21 Jun 2014 10:52:44 +0000
|
|
Message-Id: <1403347964894-790a5296-0eb7c7c7-6440334f@localhost>
|
|
MIME-Version: 1.0
|
|
```
|
|
|
|
If the node is the root node, then `Date` and `Message-Id` values are generated automatically if missing
|
|
|
|
## setContent
|
|
|
|
Sets body content for current node. If the value is a string and Content-Type is text/* then charset is set automatically.
|
|
If the value is a Buffer or a Stream you need to specify the charset yourself.
|
|
|
|
```javascript
|
|
node.setContent(body)
|
|
```
|
|
|
|
Where
|
|
|
|
* **body** - *String|Buffer|Stream|Object* body content
|
|
|
|
If the value is an object, it should include one of the following properties
|
|
|
|
* **path** - path to a file that will be used as the content
|
|
* **href** - URL that will be used as the content
|
|
|
|
**Example**
|
|
|
|
```javascript
|
|
new BuildMail('text/plain').setContent('Hello world!');
|
|
|
|
new BuildMail('text/plain; charset=utf-8').setContent(fs.createReadStream('message.txt'));
|
|
```
|
|
|
|
## build
|
|
|
|
Builds the rfc2822 message from the current node. If this is a root node, mandatory header fields are set if missing (Date, Message-Id, MIME-Version)
|
|
|
|
```javascript
|
|
node.build(callback)
|
|
```
|
|
|
|
Callback returns the rfc2822 message as a Buffer
|
|
|
|
**Example**
|
|
|
|
```javascript
|
|
new BuildMail('text/plain').setContent('Hello world!').build(function(err, mail){
|
|
console.log(mail.toString('ascii'));
|
|
});
|
|
```
|
|
|
|
Returns the following string:
|
|
|
|
```
|
|
Content-type: text/plain
|
|
Date: <current datetime>
|
|
Message-Id: <generated value>
|
|
MIME-Version: 1.0
|
|
|
|
Hello world!
|
|
```
|
|
|
|
## createReadStream
|
|
|
|
If you manage large attachments you probably do not want to generate but stream the message.
|
|
|
|
```javascript
|
|
var stream = node.createReadStream(options)
|
|
```
|
|
|
|
Where
|
|
|
|
* **options** - *Object* optional Stream options (ie. `highWaterMark`)
|
|
|
|
**Example**
|
|
|
|
```javascript
|
|
var message = new BuildMail();
|
|
message.addHeader({
|
|
from: 'From <from@example.com>',
|
|
to: 'receiver1@example.com',
|
|
cc: 'receiver2@example.com'
|
|
});
|
|
message.setContent(fs.createReadStream('message.txt'));
|
|
message.createReadStream().pipe(fs.createWriteStream('message.eml'));
|
|
```
|
|
|
|
## transform
|
|
|
|
If you want to modify the created stream, you can add transform streams that the output will be piped through.
|
|
|
|
```javascript
|
|
node.transform(transformStream)
|
|
```
|
|
|
|
Where
|
|
|
|
* **transformStream** - *Stream* or *Function* Transform stream that the output will go through before returing with `createReadStream`. If the value is a function the function should return a transform stream object when called.
|
|
|
|
**Example**
|
|
|
|
```javascript
|
|
var PassThrough = require('stream').PassThrough;
|
|
var message = new BuildMail();
|
|
message.addHeader({
|
|
from: 'From <from@example.com>',
|
|
to: 'receiver1@example.com',
|
|
cc: 'receiver2@example.com'
|
|
});
|
|
message.setContent(fs.createReadStream('message.txt'));
|
|
message.transform(new PassThrough()); // add a stream that the output will be piped through
|
|
message.createReadStream().pipe(fs.createWriteStream('message.eml'));
|
|
```
|
|
|
|
## setEnvelope
|
|
|
|
Set envelope object to use. If one is not set, it is generated based ong the headers.
|
|
|
|
```javascript
|
|
node.setEnvelope(envelope)
|
|
```
|
|
|
|
Where
|
|
|
|
* **envelope** is an envelope object in the form of `{from:'address', to: ['addresses']}`
|
|
|
|
## getEnvelope
|
|
|
|
Generates a SMTP envelope object. Makes sense only for root node.
|
|
|
|
```javascript
|
|
var envelope = node.generateEnvelope()
|
|
```
|
|
|
|
Method returns the envelope in the form of `{from:'address', to: ['addresses']}`
|
|
|
|
**Example**
|
|
|
|
```javascript
|
|
new BuildMail().
|
|
addHeader({
|
|
from: 'From <from@example.com>',
|
|
to: 'receiver1@example.com',
|
|
cc: 'receiver2@example.com'
|
|
}).
|
|
getEnvelope();
|
|
```
|
|
|
|
Returns the following object:
|
|
|
|
```json
|
|
{
|
|
'from': 'from@example.com',
|
|
'to': ['receiver1@example.com', 'receiver2@example.com']
|
|
}
|
|
```
|
|
|
|
## getAddresses
|
|
|
|
Returns an address container object. Includes all parsed addresses from From, Sender, To, Cc, Bcc and Reply-To fields.
|
|
|
|
While `getEnvelope()` returns 'from' value as a single address (the first one encountered) then `getAddresses` return all values as arrays, including `from`. Additionally while `getEnvelope` returns only `from` and a combined `to` value then `getAddresses` returns all fields separately.
|
|
|
|
Possbile return values (all arrays in the form of `[{name:'', address:''}]`):
|
|
|
|
* **from**
|
|
* **sender**
|
|
* **'reply-to'**
|
|
* **to**
|
|
* **cc**
|
|
* **bcc**
|
|
|
|
If no addresses were found for a particular field, the field is not set in the response object.
|
|
|
|
**Example**
|
|
|
|
```javascript
|
|
new BuildMail().
|
|
addHeader({
|
|
from: 'From <from@example.com>',
|
|
to: '"Receiver" receiver1@example.com',
|
|
cc: 'receiver2@example.com'
|
|
}).
|
|
getAddresses();
|
|
```
|
|
|
|
Returns the following object:
|
|
|
|
```javascript
|
|
{
|
|
from: [{
|
|
name: 'From',
|
|
address: 'from@example.com'
|
|
}],
|
|
to: [{
|
|
name: 'Receiver',
|
|
address: 'receiver1@example.com'
|
|
}],
|
|
cc: [{
|
|
name: '',
|
|
address: 'receiver2@example.com'
|
|
}]
|
|
}
|
|
```
|
|
|
|
## Notes
|
|
|
|
### Addresses
|
|
|
|
When setting address headers (`From`, `To`, `Cc`, `Bcc`) use of unicode is allowed. If needed
|
|
the addresses are converted to punycode automatically.
|
|
|
|
### Attachments
|
|
|
|
For attachments you should minimally set `filename` option and `Content-Disposition` header. If filename is specified, you can leave content type blank - if content type is not set, it is detected from the filename.
|
|
|
|
```javascript
|
|
new BuildMail('multipart/mixed').
|
|
createChild(false, {filename: 'image.png'}).
|
|
setHeader('Content-Disposition', 'attachment');
|
|
```
|
|
|
|
Obviously you might want to add `Content-Id` header as well if you want to reference this attachment from the HTML content.
|
|
|
|
### MIME structure
|
|
|
|
Most probably you only need to deal with the following multipart types when generating messages:
|
|
|
|
* **multipart/alternative** - includes the same content in different forms (usually text/plain + text/html)
|
|
* **multipart/related** - includes main node and related nodes (eg. text/html + referenced attachments). Also requires a `type` parameter that indicates the Content-Type of the *root* element in the node
|
|
* **multipart/mixed** - includes other multipart nodes and attachments, or single content node and attachments
|
|
|
|
**Examples**
|
|
|
|
One content node and an attachment
|
|
|
|
```
|
|
multipart/mixed
|
|
↳ text/plain
|
|
↳ image/png
|
|
```
|
|
|
|
Content node with referenced attachment (eg. image with `Content-Type` referenced by `cid:` url in the HTML)
|
|
|
|
```
|
|
multipart/related
|
|
↳ text/html
|
|
↳ image/png
|
|
```
|
|
|
|
Plaintext and HTML alternatives
|
|
|
|
```
|
|
multipart/alternative
|
|
↳ text/html
|
|
↳ text/plain
|
|
```
|
|
|
|
One content node with referenced attachment and a regular attachment
|
|
|
|
```
|
|
multipart/mixed
|
|
↳ multipart/related
|
|
↳ text/plain
|
|
↳ image/png
|
|
↳ application/x-zip
|
|
```
|
|
|
|
Alternative content with referenced attachment for HTML and a regular attachment
|
|
|
|
```
|
|
multipart/mixed
|
|
↳ multipart/alternative
|
|
↳ text/plain
|
|
↳ multipart/related
|
|
↳ text/html
|
|
↳ image/png
|
|
↳ application/x-zip
|
|
```
|
|
|
|
## License
|
|
|
|
**MIT** |