mime-model

1.1.0 • Public • Published

mime-model

Create and parse MIME nodes.

See the examples folder for usage examples.


This project is supported by Opensense - The beautiful email signature management company for Office 365 and Google Workspace.

Usage

$ npm install mime-model
const { MimeNode } = require('mime-model');

Parse from EML

const eml = fs.readFileSync('path/to/email.eml');
const node = MimeNode.from(eml);

Create from scratch

const node = MimeNode.create(contentType, {
    // content options
});

See possible content options below.

In addition to normal content options, you can use the following special options:

  • defaultHeaders - if true then sets default headers like Date, Message-ID and MIME-Version

Serialize

Serialize a mime node back to an EML file.

node.serialize([opts]) -> Buffer

Where opts is an object that limits ouput:

  • ops.headers if true will only return headers
  • ops.body if true will only return body of the node

If both are true, an empty value is returned.

Example

const node = MimeNode.create('text/plain', {
    encoding: 'quoted-printable',
    content: 'Hello world 🔆!',
    defaultHeaders: true
});
process.stdout.write(node.serialize());

Output

Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable
Message-ID: <64300d0d-022d-4ad3-9212-ad84116ef922@mim>
Date: Sun, 06 Nov 2022 15:51:38 +0000
MIME-Version: 1.0

Hello world =F0=9F=94=86!

multipart

The multipart property contains the multipart type for the node, like "mixed" or "alternative", etc. The value is null if this node is not a multipart node.

node.multipart -> String

Example

if (node.multipart) {
    for (let childNode of node.childNodes) {
        // ...
    }
}

childNodes

The chldNodes property contains an array of child nodes for a multipart node.

node.childNodes -> Array

Example

Prints out a tree structure of Content-Type values by traversing all child nodes in the MIME tree.

let walkNodes = (node, level) => {
    console.log('  '.repeat(level) + node.contentType);
    if (node.multipart) {
        for (let childNode of node.childNodes) {
            walkNodes(childNode, level + 1);
        }
    }
};
walkNodes(rootNode, 0);

Example output:

multipart/mixed
  multipart/alternative
    text/plain
    text/html
  image/png

Add a child node to a multipart node

node.appendChild(newNode);

or

node.insertBefore(newNode, referenceNode);

Example

const node = MimeNode.create('multipart/mixed', {
    // set headers like Message-ID, Date, etc
    defaultHeaders: true
});

const textNode = MimeNode.create('text/plain', {
    encoding: 'quoted-printable',
    content: 'Hello world 🔆!'
});
node.appendChild(textNode);

Remove a child node

Removes a child node from the parent node and returns the removed node.

parentNode.removeChild(childNode);

or

childNode.delete();

Check Content-Type

MIME-type of the node or null if not set.

node.contentType -> String

Example

if (node.contentType === 'image/png') {
    console.log('Image attachment');
}

Node editing

All the following properties can be used as content options when creating new nodes using MimeNode.create().

subject

Read or set the subject line. Unicode strings are used by default, so there is no need to encode or decode anything. Value is null if the subject is not set.

console.log(node.subject); // "Subject line 👀"
node.subject = 'Another subject ✅';

date

Read or set date objects. Value is null if the date is not set.

console.log(node.date); // 2022-11-05T19:51:24.992Z (Date object)
node.date = node.date.getTime() + 1000; // number is coerced to a date object

encoding

Read or set Content-Transfer-Encoding for a node.

// change content transfer encoding of a node from base64 to quoted-printable
if (node.encoding === 'base64') {
    node.encoding = 'quoted-printable';
}

charset

Read or set the character set for a text content node.

// enforce UTF-8 for ISO-2022-JP content
if (node.charset === 'iso-2022-jp') {
    node.charset = 'utf-8';
}

content

Read or set data content. When reading, the value is a buffer in the character set of the node. Transfer encoding is handled silently, so the value is the actual content, not a base64 or quoted-printable string.

When writing a string, it is encoded to the charset of the node automatically. Buffer values are not modified.

If you need to read an unicode string, not a buffer value, use node.contentText.

// content is binary buffer without encoding
let imageFile = imageNode.content;
fs.writeFileSync('image.png', imageFile);

// charsets for strings are handled silently in the background
textNode.content = textNode.contentText + ' suffix value';

filename

Read or set the file name for the node.

imageNode.filename = 'Image ✅.png';

disposition

Read or set Content-Disposition value.

NB! This is not full header, but only the disposition identifier like "attachment" or "inline" or null if not set.

imageNode.disposition = 'inline';

contentId

Read or set Content-Id value.

imageNode.contentId = '<unique-id@example.com>';

addresses

Read and set address field values. The returned value is a structured object with unicode strings. When setting an address value, you can use structured objects or a full header string without encoding.

  • node.from
  • node.to
  • node.cc
  • node.bcc
  • node.sender
  • node.replyTo

Example

node.from = 'Juulius 📭 <juulius@example.com>';
node.to = [{ name: 'Mõdu 🍯', address: 'modu@example.com' }];

console.log(node.from);
console.log(node.to);

// [ { address: 'juulius@example.com', name: 'Juulius 📭' } ]
// [ { name: 'Mõdu 🍯', address: 'modu@example.com' } ]

process.stdout.write(node.serialize({ headers: true }));

// From: =?UTF-8?Q?Juulius_=F0=9F=93=AD?= <juulius@example.com>
// To: =?UTF-8?B?TcO1ZHUg8J+Nrw==?= <modu@example.com>

messageId

Read or set Message-Id value.

console.log(rootNode.messageId);
// " <28b35c92-852a-4862-b654-72e25f091223@example.com>"

headers

Read node headers. The value is a list of tuples with header keys and values. The ordering is the same as in the serialized header.

node.headers -> Array

Example

console.log(node.headers);
[
  [
    'Subject', '=?UTF-8?Q?Nodemailer_is_unicode_friendly_=E2=9C=94?= =?UTF-8?Q?1656663957583?='
  ],
  [ 'Content-Type', 'text/plain; charset=utf-8' ],
  [ 'Content-Transfer-Encoding', 'quoted-printable' ],
  [ 'Content-Disposition', 'inline' ],
  [ 'Message-ID', '<3660bb9f-d645-4945-a492-2c291de3e6fb@mim>' ],
  [ 'Date', 'Sun, 06 Nov 2022 13:44:10 +0000' ],
  [ 'MIME-Version', '1.0' ]
]

Get specific header value

Returns a list of parsed and encoded values for a header key. Each header key has a specific format.

node.getHeader(key) -> Array

The response is always an array as any header key can be defined multiple times. If you only need the oldest/bottom value, use the last entry from the array.

Example

Content-Disposition: attachment;
 filename*0*=utf-8''nyan%20cat%20%E2%9C%94.gif
let headerValueArray = node.getHeader('content-disposition');
console.log(headerValueArray[0].params.filename);
// "nyan cat ✔.gif"

resetContent()

Clears node content and sets a new content type value. Primarily useful if you want to convert a content node to a multipart node. See the setBody() example for usage.

node.resetContent(contentType);

Example

rootNode.resetContent('multipart/signed; protocol="application/pgp-signature"; micalg=pgp-sha512;');

NB MIME boundary is generated automatically for multipart nodes

removeHeaders()

Removes and returns headers with a specific key value. Useful when you want to move headers from one node to another.

node.removeHeaders(key) -> Array

Example

Move Content-Type headers from one node to another

const contentTypeHeaders = rootNode.removeHeaders('Content-Type');
bodyMimeNode.setHeaders(contentTypeHeaders);

setHeaders()

Adds headers to a node. The input array either includes header entry objects returned by removeHeaders() or full header strings.

node.setHeaders(headersArray);

Example

node.setHeaders([`Subject: =?UTF-8?Q?Nodemailer_is_unicode_friendly_=E2=9C=94?= =?UTF-8?Q?1656663957583?=`]);
console.log(node.subject); // "Nodemailer is unicode friendly ✔1656663957583"

removeBody()

Removes and returns body structure from a node. Useful when you want to move body contents from one node to another.

node.removeBody() -> Object

This method works both for content nodes and multipart nodes.

setBody()

Attach a body object from one node to another. You can get this object from the removeBody() call.

node.setBody(bodyObj) -> Object

This method works both for content nodes and multipart nodes.

Example

Convert a regular mime node into a multipart node.

// remove body and relevant headers from the root node
const contentTypeHeaders = rootNode.removeHeaders('Content-Type');
const contentTransferEncodingHeaders = rootNode.removeHeaders('Content-Transfer-Encoding');
const mimeBody = rootNode.removeBody();

// create a new empty node and attach headers and body
const childNode = MimeNode.create(null);
childNode.setHeaders(contentTypeHeaders);
childNode.setHeaders(contentTransferEncodingHeaders);
childNode.setBody(mimeBody);

// force the root node into a multipart node and attach the child node to it
rootNode.resetContent('multipart/mixed');
rootNode.appendChild(childNode);

License

MIT

Readme

Keywords

Package Sidebar

Install

npm i mime-model

Weekly Downloads

1

Version

1.1.0

License

MIT

Unpacked Size

344 kB

Total Files

33

Last publish

Collaborators

  • andris