DEV Community

Cover image for How Internet Message Access Protocol(IMAP) works in Node JS
Venkat
Venkat

Posted on

How Internet Message Access Protocol(IMAP) works in Node JS

Hello my dear peers 😃! Hope you're doing well. Welcome to my tech blog and this time we are discussing about IMAP package and it's uses in Node JS with real time code snippet examples. In this, first will only focus on reading emails.

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

Let's open our terminal and hit npm install node-imap. to install IMAP package.

In this blog, we are mainly focusing on how to read email attachments based on the DATE RANGE, FROM particular email address and it's SUBJECT.

Let's see from the below example code which fetches first 3 email messages from the mail box.

var Imap = require('node-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();
Enter fullscreen mode Exit fullscreen mode

There are scenarios where you need to fetch only the attachments from the email and process it for a different purpose. In such cases, please refer below code example.

var imap = new Imap({
  user: 'mygmailname@gmail.com',
  password: 'mygmailpassword',
  host: 'imap.gmail.com',
    port: 993,
    tls: true,
  });
  imap.once("ready", function () {
    var fs = require("fs"),
      fileStream;
    imap.openBox("INBOX", true, function (err, box) {
      if (err) throw err;
      try {
        imap.search(
          [
            ["FROM", FROM_MAIL],
            ["HEADER", "SUBJECT", SUBJECT],
            ["UNSEEN", ["SINCE", "Day, Year"]],
          ],
          function (err, results) {
            if (err) throw err;
            try {
              var f = imap.fetch(results, {
                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",
                      Imap.parseHeader(buffer)
                    );
                  });
                });
                msg.once("attributes", function (attrs) {
                  // console.log("test", attrs);
                  var attachments = findAttachmentParts(attrs.struct);
                  console.log(
                    prefix + "Has attachments: %d",
                    attachments.length
                  );
                  for (var i = 0, len = attachments.length; i < len; ++i) {
                    var attachment = attachments[i];

                    var f = imap.fetch(attrs.uid, {
                      //do not use imap.seq.fetch here
                      bodies: [attachment.partID],
                      struct: true,
                    });
                    //build function to process attachment message
                    f.on("message", processAttachment(attachment));
                  }
                });
                msg.once("end", function () {
                  console.log(prefix + "Finished email");
                });
              });
              f.once("error", function (err) {
                console.log("Fetch error: " + err);
              });
              f.once("end", function () {
                console.log("Done fetching all messages!");
                imap.end();
              });
            } catch (e) {
              console.log("err", e);
            }
          }
        );
      } catch (e) {
        console.log("log", e);
      }
    });
  });

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

  imap.once("end", function () {
    console.log("Connection ended");
  });
  imap.connect();
Enter fullscreen mode Exit fullscreen mode

The downloaded email attachment must be decoded using Base64Decode() method.

function processAttachment(attachment) {
  var filename = attachment.params.name;
  var encoding = attachment.encoding;
  var name = filename.split(".")[1];
  console.log("log", name);

  return function (msg, seqno) {
    if (name === "pdf") {
      var prefix = "(#" + seqno + ") ";
      msg.on("body", function (stream, info) {
        //Create a write stream so that we can stream the attachment to file;
        console.log(
          prefix + "Streaming this attachment to file",
          filename,
          info
        );
        var path = require("path");
       // var dirPath = path.join(__dirname, "/attachments");
        var writeStream = fs.createWriteStream(filename);
        writeStream.on("finish", function () {
          console.log(prefix + "Done writing to file %s", filename);
        });

        if (toUpper(encoding) === "BASE64") {
          stream.pipe(new base64.Base64Decode()).pipe(writeStream);
        } else {
          stream.pipe(writeStream);
        }
      });
      msg.once("end", function () {
        console.log(prefix + "Finished attachment %s", filename);
      });
    }
  };
}
Enter fullscreen mode Exit fullscreen mode

Note: The above process attachment method has a condition check of having only PDF docs.

So, after processing the email attachments would you recommend those emails still be present in same inbox? No not at all, because we need to move that to some other folder so that we can differentiate the newly arrived emails.

So, you can move the processed email to specific folder from the inbox using below code example.

 imap.seq.move(seqno, "Processed", function (err) {
                  if (!err) {
                    console.log(seqno + ": move success");
                  }
                });
Enter fullscreen mode Exit fullscreen mode

Hope you got atleast an idea how to work with imap package and with emails in Node JS 🎉🎉.

References:
https://www.npmjs.com/package/node-imap
https://github.com/mikebevz/node-imap

Thank you for sticking around and holding on to the end.

Until next time!

Discussion (0)