node-imap

repository·master·Indexed 24 days ago

https://github.com/mscdex/node-imap

A low-level IMAP client for Node.js (version 0.8.19) that provides raw access to IMAP servers. It allows for mailbox management, message searching and fetching, flag/keyword manipulation, and supports Gmail extensions (X-GM-EXT-1) and RFC2087 quota management. The library provides raw access to server data without automatic decoding of attachments or email address parsing.

Tokens
5.9K
Snippets
4
Records
27
Agent score
31%

What's inside node-imap

  1. Use CONDSTORE for modification sequence tracking

    master

    If the server supports the CONDSTORE capability (RFC4551), you can track changes using the modification sequence (modseq).

    Key features:

    • Connection 'update' event: May contain a modseq property (string) representing the new modification sequence value.
    • search() with MODSEQ: Using MODSEQ as a criteria changes the callback signature to callback(err, UIDs, modseq). The modseq returned is the highest modification sequence value among the results.
    • fetch() with changedsince: You can use the changedsince modifier (string) to only fetch messages that have changed since a specific modseq.
    • Box status: The Box type returned by openBox() or status() may include a highestmodseq property (string).

    Conditional Flag/Keyword methods:

    The following methods allow performing actions only on messages that have not changed since a specific modseq:

    • addFlagsSince(source, flags, modseq, callback)
    • delFlagsSince(source, flags, modseq, callback)
    • setFlagsSince(source, flags, modseq, callback)
    • addKeywordsSince(source, keywords, modseq, callback)
    • delKeywordsSince(source, keywords, modseq, callback)
    • setKeywordsSince(source, keywords, modseq, callback)

    Note: All these methods have seqno-based counterparts.

  2. Gmail Extension Support

    master

    If the server supports the X-GM-EXT-1 capability, you can use Gmail-specific features.

    Use the X-GM-RAW criteria in search() to pass Gmail's custom search syntax (e.g., 'has:attachment in:unread').

    Gmail Metadata

    When using fetch(), the following are automatically retrieved:

    • x-gm-thrid: Conversation/thread ID
    • x-gm-msgid: Account-wide unique ID
    • x-gm-labels: Gmail labels

    Gmail Label Management

    Additional methods are available for managing Gmail labels:

    • setLabels(source, labels, callback)
    • addLabels(source, labels, callback)
    • delLabels(source, labels, callback)
  3. Install node-imap via npm

    master

    Install the imap package using npm to use it as an IMAP client for Node.js.

    Requirements:

    • Node.js v10.0.0 or newer
    • An IMAP server (e.g., Gmail)
    npm install imap
  4. Retrieve and buffer the newest message body

    master

    To get the full content of the latest message, use imap.seq.fetch() with the TEXT body part specifier. You can use the box.messages.total property to identify the last message in the mailbox.

    // 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();
      });
    });
  5. Search and save unread emails to files

    master

    You can use imap.search() to find messages matching specific criteria (e.g., UNSEEN or SINCE) and then pipe the resulting body streams directly to a file using Node.js fs modules.

    // 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();
        });
      });
    });
  6. Fetch message headers and structure

    master

    You can fetch specific message headers and the message structure using imap.seq.fetch(). This is useful for inspecting metadata without downloading the entire body.

    In the example below, we fetch the FROM, TO, SUBJECT, and DATE headers and request the struct: true to see the message parts.

    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();
  7. Manage message flags and keywords

    master

    Use the following methods to modify message metadata in the currently open mailbox. All methods take a <MessageSource> (UIDs/sequence numbers) and a callback (err) => void.

    Flags (System Flags)

    • addFlags(source, flags, callback): Adds flag(s) (e.g., ['Seen', 'Flagged']).
    • delFlags(source, flags, callback): Removes flag(s).
    • setFlags(source, flags, callback): Sets the flag(s).

    Keywords (User Flags)

    • addKeywords(source, keywords, callback): Adds keyword(s).
    • delKeywords(source, keywords, callback): Removes keyword(s).
    • setKeywords(source, keywords, callback): Sets keyword(s).
  8. Fetch message content with fetch()

    master

    The fetch(source, options) method retrieves message data from the currently open mailbox.

    Parameters:

    • source: The <MessageSource> (typically UIDs or sequence numbers).
    • options: An optional object to specify what parts of the message to retrieve.

    Common options properties:

    • markSeen: (boolean) Mark messages as read. Default: false.
    • struct: (boolean) Fetch message structure. Default: false.
    • envelope: (boolean) Fetch the message envelope. Default: false.
    • size: (boolean) Fetch the RFC822 size. Default: false.
    • bodies: (string|Array) Specifies which body parts to fetch.

    Body Section Examples:

    • 'HEADER': The message header.
    • 'HEADER.FIELDS (TO FROM)': Specific header fields.
    • 'TEXT': The message body.
    • '': The entire message (header + body).
    • 'MIME': MIME-related header fields.
    • Part IDs: You can prefix sections with part IDs, e.g., '1.TEXT' or '2.MIME'.
  9. Parse Raw Email Headers

    master

    Use the static parseHeader method to convert a raw IMAP header string into a structured object. The resulting object is keyed by header fields, and each value is an array of strings (to handle multiple occurrences of the same header).

    • parseHeader(rawHeader[, disableAutoDecode])
    • disableAutoDecode: If true, prevents automatic decoding of MIME encoded-words.
  10. Manage Mailboxes (Boxes)

    master

    The Connection instance provides methods to manipulate the mailbox structure on the server:

    • openBox(mailboxName[, openReadOnly=false, modifiers], callback): Opens a mailbox. Callback returns (err, mailbox).
    • closeBox([autoExpunge=true], callback): Closes the current mailbox. If autoExpunge is true, deleted messages are removed (unless in read-only mode).
    • addBox(mailboxName, callback): Creates a new mailbox.
    • delBox(mailboxName, callback): Removes a mailbox.
    • renameBox(oldMailboxName, newMailboxName, callback): Renames a mailbox. Renaming 'INBOX' moves its messages to the new mailbox.
    • subscribeBox(mailboxName, callback): Subscribes to a mailbox.
    • unsubscribeBox(mailboxName, callback): Unsubscribes from a mailbox.
    • status(mailboxName, callback): Fetches info for a mailbox (do not use on the currently open mailbox). Callback returns (err, mailbox).
    • getBoxes([nsPrefix], callback): Gets the full list of mailboxes. Callback returns (err, boxes).
    • getSubscribedBoxes([nsPrefix], callback): Gets the list of subscribed mailboxes. Callback returns (err, boxes).
  11. Group search results with thread()

    master

    If the server supports THREAD=REFERENCES or THREAD=ORDEREDSUBJECT, use thread() to group search results into threads.

    Signature: thread(algorithm, searchCriteria, callback)

    Callback parameters:

    • err: Error object
    • UIDs: A nested array of UIDs representing the threads.

    Algorithms:

    • 'references'
    • 'orderedsubject'

    Note: A seqno-based counterpart also exists.

  12. Search for messages using search()

    master

    The search(criteria, callback) method searches the currently open mailbox for messages matching the provided criteria.

    Key Behaviors:

    • Criteria Format: criteria is an array. For types requiring arguments, use a nested array (e.g., ['FROM', 'user@example.com']).
    • Negation: Prefix a criteria type with ! to negate it (e.g., ['!SEEN']).
    • Logic: By default, all criteria in the array are ANDed together. To use OR logic, use the special 'OR' keyword on exactly two criteria.
    • UID vs Sequence Numbers: By default, search() returns UIDs. To use sequence numbers instead, use the conn.seq.search() namespace.

    Callback Parameters:

    • err: <Error>
    • UIDs: <array> of found UIDs.