mscdex / node-imap

An IMAP client module for node.js.
MIT License
2.14k stars 379 forks source link

Description

node-imap is an IMAP client module for node.js.

This module does not perform any magic such as auto-decoding of messages/attachments or parsing of email addresses (node-imap leaves all mail header values as-is).

An upgrade guide from node-imap v0.7.x to v0.8.x can be found here.

Requirements

Installation

npm install imap

Examples

var Imap = require('imap'),
    inspect = require('util').inspect;

var imap = new Imap({
  user: 'mygmailname@gmail.com',
  password: 'mygmailpassword',
  host: 'imap.gmail.com',
  port: 993,
  tls: true
});

function openInbox(cb) {
  imap.openBox('INBOX', true, cb);
}

imap.once('ready', function() {
  openInbox(function(err, box) {
    if (err) throw err;
    var f = imap.seq.fetch('1:3', {
      bodies: 'HEADER.FIELDS (FROM TO SUBJECT DATE)',
      struct: true
    });
    f.on('message', function(msg, seqno) {
      console.log('Message #%d', seqno);
      var prefix = '(#' + seqno + ') ';
      msg.on('body', function(stream, info) {
        var buffer = '';
        stream.on('data', function(chunk) {
          buffer += chunk.toString('utf8');
        });
        stream.once('end', function() {
          console.log(prefix + 'Parsed header: %s', inspect(Imap.parseHeader(buffer)));
        });
      });
      msg.once('attributes', function(attrs) {
        console.log(prefix + 'Attributes: %s', inspect(attrs, false, 8));
      });
      msg.once('end', function() {
        console.log(prefix + 'Finished');
      });
    });
    f.once('error', function(err) {
      console.log('Fetch error: ' + err);
    });
    f.once('end', function() {
      console.log('Done fetching all messages!');
      imap.end();
    });
  });
});

imap.once('error', function(err) {
  console.log(err);
});

imap.once('end', function() {
  console.log('Connection ended');
});

imap.connect();
// using the functions and variables already defined in the first example ...

openInbox(function(err, box) {
  if (err) throw err;
  var f = imap.seq.fetch(box.messages.total + ':*', { bodies: ['HEADER.FIELDS (FROM)','TEXT'] });
  f.on('message', function(msg, seqno) {
    console.log('Message #%d', seqno);
    var prefix = '(#' + seqno + ') ';
    msg.on('body', function(stream, info) {
      if (info.which === 'TEXT')
        console.log(prefix + 'Body [%s] found, %d total bytes', inspect(info.which), info.size);
      var buffer = '', count = 0;
      stream.on('data', function(chunk) {
        count += chunk.length;
        buffer += chunk.toString('utf8');
        if (info.which === 'TEXT')
          console.log(prefix + 'Body [%s] (%d/%d)', inspect(info.which), count, info.size);
      });
      stream.once('end', function() {
        if (info.which !== 'TEXT')
          console.log(prefix + 'Parsed header: %s', inspect(Imap.parseHeader(buffer)));
        else
          console.log(prefix + 'Body [%s] Finished', inspect(info.which));
      });
    });
    msg.once('attributes', function(attrs) {
      console.log(prefix + 'Attributes: %s', inspect(attrs, false, 8));
    });
    msg.once('end', function() {
      console.log(prefix + 'Finished');
    });
  });
  f.once('error', function(err) {
    console.log('Fetch error: ' + err);
  });
  f.once('end', function() {
    console.log('Done fetching all messages!');
    imap.end();
  });
});
// using the functions and variables already defined in the first example ...

var fs = require('fs'), fileStream;

openInbox(function(err, box) {
  if (err) throw err;
  imap.search([ 'UNSEEN', ['SINCE', 'May 20, 2010'] ], function(err, results) {
    if (err) throw err;
    var f = imap.fetch(results, { bodies: '' });
    f.on('message', function(msg, seqno) {
      console.log('Message #%d', seqno);
      var prefix = '(#' + seqno + ') ';
      msg.on('body', function(stream, info) {
        console.log(prefix + 'Body');
        stream.pipe(fs.createWriteStream('msg-' + seqno + '-body.txt'));
      });
      msg.once('attributes', function(attrs) {
        console.log(prefix + 'Attributes: %s', inspect(attrs, false, 8));
      });
      msg.once('end', function() {
        console.log(prefix + 'Finished');
      });
    });
    f.once('error', function(err) {
      console.log('Fetch error: ' + err);
    });
    f.once('end', function() {
      console.log('Done fetching all messages!');
      imap.end();
    });
  });
});

API

Data types

A message structure with multiple parts might look something like the following:

[ { type: 'mixed',
    params: { boundary: '000e0cd294e80dc84c0475bf339d' },
    disposition: null,
    language: null,
    location: null
  },
  [ { type: 'alternative',
      params: { boundary: '000e0cd294e80dc83c0475bf339b' },
      disposition: null,
      language: null
    },
    [ { partID: '1.1',
        type: 'text',
        subtype: 'plain',
        params: { charset: 'ISO-8859-1' },
        id: null,
        description: null,
        encoding: '7BIT',
        size: 935,
        lines: 46,
        md5: null,
        disposition: null,
        language: null
      }
    ],
    [ { partID: '1.2',
        type: 'text',
        subtype: 'html',
        params: { charset: 'ISO-8859-1' },
        id: null,
        description: null,
        encoding: 'QUOTED-PRINTABLE',
        size: 1962,
        lines: 33,
        md5: null,
        disposition: null,
        language: null
      }
    ]
  ],
  [ { partID: '2',
      type: 'application',
      subtype: 'octet-stream',
      params: { name: 'somefile' },
      id: null,
      description: null,
      encoding: 'BASE64',
      size: 98,
      lines: null,
      md5: null,
      disposition:
       { type: 'attachment',
         params: { filename: 'somefile' }
       },
      language: null,
      location: null
    }
  ]
]

The above structure describes a message having both an attachment and two forms of the message body (plain text and HTML). Each message part is identified by a partID which is used when you want to fetch the content of that part (see fetch()).

The structure of a message with only one part will simply look something like this:

[ { partID: '1',
    type: 'text',
    subtype: 'plain',
    params: { charset: 'ISO-8859-1' },
    id: null,
    description: null,
    encoding: '7BIT',
    size: 935,
    lines: 46,
    md5: null,
    disposition: null,
    language: null
  }
]

Therefore, an easy way to check for a multipart message is to check if the structure length is >1.

Lastly, here are the system flags defined by RFC3501 that may be added/removed:

It should be noted however that the IMAP server can limit which flags can be permanently modified for any given message. If in doubt, check the mailbox's permFlags first. Additional custom flags may be provided by the server. If available, these will also be listed in the mailbox's permFlags.

require('imap') returns one object: Connection.

Connection Events

Connection Properties

Connection Static Methods

Connection Instance Methods

Note: Message UID ranges are not guaranteed to be contiguous.

All functions below have sequence number-based counterparts that can be accessed by using the 'seq' namespace of the imap connection's instance (e.g. conn.seq.search() returns sequence number(s) instead of UIDs, conn.seq.fetch() fetches by sequence number(s) instead of UIDs, etc):

Extensions Supported

TODO

Several things not yet implemented in no particular order: