DEV Community

loading...

Manage Your're Mail Using IMAP

mesadhan profile image Sadhan Sarker ・Updated on ・5 min read

πŸ“— IMAP Overview

If you have ever set up an email client or app, you will have certainly come across the terms POP and IMAP.
IMAP-is short for Internet Message Access Protocol, while POP-translates to Post Office Protocol. In other words, both are email protocols. You might listen about Outlook, Thunderbird, Eudora, GNUMail, or (Mac) Mail app which provides custom mail management services they download messages from your email account and to make changes like archiving messages or sorting them into folders and giving more personalization.

Essentially, the main difference of the two protocols is that POP downloads emails from the server for permanent local storage, while IMAP leaves them on the server and just caches (temporarily stores) emails locally. In other words, IMAP is a form of cloud storage.

πŸ“— When Choose POP:-

  • if you want to access your mail from only one single device.
  • if need constant access to your email, regardless of internet availability.
  • if you have limited server storage.

πŸ“— When Choose IMAP:-

  • if want to access your email from multiple different devices.
  • if you have a reliable and constant internet connection.
  • if you want to receive a quick overview of new emails or emails on the server.
  • if local storage space is limited.
  • if worried about backing your emails up.

If confused, then go with IMAP. It’s the more modern protocol, it allows you to be flexible, and your email is automatically backed up on the server.

Today, we're going to look into IMAP, It's the open standard that describes how to access messages in an email mailbox. While we want to read and send mail programmatically, then IMAP is important. we just need an IMAP client with implemented on top of IMAP protocol standards.

Now let's deep dive into it. We’re going to connect to an email account, list all of the available folders in the mailbox, and download a message. Here, we are going to use NodeJS IMAP Client which will help us to explore,

πŸ“— Enable IMAP permission for your E-Mail

πŸ”° Now, Setup IMAP Client

Here, I'm using nodejs client but you can use other language clients to achieve those features,

Before creating a node application make sure node is installed in your machine, You can find the installation guide from here [https://nodejs.org/en/download/].

node --version
npm --version
Enter fullscreen mode Exit fullscreen mode

πŸ”°Create a node application,

mkdir imap-client-app
npm init -y
npm install --save imap
Enter fullscreen mode Exit fullscreen mode

Note: NodeJs library Node-IMAP Client at Github.

πŸ“— Take a look at implementation,

πŸ”° Now, create manage-mailbox.js files

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


let getEmailFromInbox = (mailServer) => {
    mailServer.openBox('INBOX', true, function (err, box) {
        if (err) throw err;
        // we can define range '1:3'
        let f = mailServer.seq.fetch('1:*', {
            bodies: 'HEADER.FIELDS (FROM TO SUBJECT DATE)',
            struct: true
        });
        f.on('message', function (msg, seqno) {
            console.log('Message #%d', seqno);
            let prefix = '(#' + seqno + ') ';
            msg.on('body', function (stream, info) {
                let buffer = '';
                stream.on('data', function (chunk) {
                    buffer += chunk.toString('utf8');
                });
                stream.once('end', function () {
                    console.log(prefix + 'Parsed header: %s', inspect(Imap.parseHeader(buffer)));
                });
            });
        });
        f.once('error', function (err) {
            console.log('Fetch error: ' + err);
        });
        f.once('end', function () {
            console.log('Done fetching all messages!');
            //mailServer.end();
        });
    });
}

let createLabel = (mailServer, labelName) => {
    mailServer.addBox(labelName, (err) => {});
    console.log('message', 'New Label or Box Created');
}

let getMailboxStatusByName = (mailServer, inboxName) => {
    mailServer.status(inboxName, (err, mailbox) => {
        console.log('message', mailbox);
    });
    console.log('message', 'Label or Box Status');
}

let getMailBoxLabels = (mailServer) => {
    mailServer.getBoxes((error, mailbox) => {
        console.log('message', mailbox);
    })
}

let deleteLabel = (mailServer, labelName) => {
    mailServer.delBox(labelName, (error) => {})
   console.log('message', 'Label or Box removed');
}

let mailServer1 = new Imap({
    user: 'example@gmail.com',
    password: 'password',
    host: 'imap.gmail.com',
    port: 993,
    tls: true,
    tlsOptions: {
        rejectUnauthorized: false
    },
    authTimeout: 3000
}).once('error', function (err) {
    console.log('Source Server Error:- ', err);
});
mailServer1.once('ready', function () {
    mailServer1.openBox('INBOX', true, function (err, box) {
        if (err) throw err;
        console.log('message', 'server1 ready');
    });

    // mail operation
    getMailBoxLabels(mailServer1);
    getEmailFromInbox(mailServer1)
    createLabel(mailServer1, "demo-label1");
    deleteLabel(mailServer1, "demo-label1");
    getMailboxStatusByName(mailServer1, "INBOX");
})
mailServer1.connect();
Enter fullscreen mode Exit fullscreen mode

πŸ”° To run that script, hit below command, see output

node manage-mailbox.js
Enter fullscreen mode Exit fullscreen mode

πŸ”° We can import and export email, one email server to another email server. To copy email create copy-mailbox.js,

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

// Here, copy email source-mail-server to target-mail-server
let copyEmail = (srcServer1, srcServer2, emailServerName) => {
    srcServer1.search(['ALL'], (error, uids) => {
        //console.log('message', uids);
        var count = uids.length;
        var f = srcServer1.fetch(uids, { bodies: 'HEADER.FIELDS (FROM TO SUBJECT DATE)', struct: true });

        f.on('message', function (msg, seqno) {
            //console.log('Got a message with seq no: ' + seqno);

            var buffer = [], bufLen = 0, bufferText = '';
            msg.on('body', function (stream, info) {
                stream.on('data', function (chunk) {
                    bufferText += chunk.toString('utf8');
                    buffer.push(chunk);
                    bufLen += chunk.length;
                });
                stream.once('end', function () {
                    console.log('Parsed header: %s', inspect(Imap.parseHeader(bufferText)));                  // email contents
                    console.log(`====${emailServerName} - Finished message no. ${seqno} =======`);            // email email seqno

                    //console.log('message-binay-buffer', buffer);
                    //console.log('message-bufLen', bufLen);
                    //console.log('message-total-email-count', count);

                    buffer = Buffer.concat(buffer, bufLen);
                    srcServer2.append(buffer, {
                        date: new Date(msg.date),
                        flags: ['Seen']
                    }, function (err) {
                        if (err) throw err;
                        if (--count === 0) {
                            console.log('Done copying!');
                            srcServer2.end()                    // close mail server1 connection
                            srcServer1.end()                    // close mail server2 connection
                        }
                    });

                });
            });
        });
        f.once('error', function (err) {
            console.log('Fetch error: ' + err);
        });
        f.once('end', function () {
            console.log('Done fetching all messages!');
        });
    });
};

let srcServer2 = new Imap({
    user: 'example-target@gmail.com',
    password: 'password',
    host: 'imap.gmail.com',
    port: 993,
    tls: true,
    tlsOptions: {
        rejectUnauthorized: false
    },
    authTimeout: 3000
}).once('error', function (err) {
    console.log('Target mail server error:- ', err);
});

srcServer2.once('ready', function () {
    srcServer2.openBox('INBOX', true, function (err, box) {
        if (err) throw err;
        console.log('message', 'Target mail server ready');
        let srcServer1 = new Imap({
            user: 'example-source@gmail.com',
            password: 'password',
            host: 'imap.gmail.com',
            port: 993,
            tls: true,
            tlsOptions: {
                rejectUnauthorized: false
            },
            authTimeout: 3000
        }).once('error', function (err) {
            console.log('Source Server Error:- ', err);
        });
        srcServer1.once('ready', function () {
            srcServer1.openBox('INBOX', true, function (err, box) {
                if (err) throw err;
                console.log('message', 'Source mail server ready');

                copyEmail(srcServer1, srcServer2, 'Server1');
            })
        });
        srcServer1.connect();
    });
})
srcServer2.connect();
Enter fullscreen mode Exit fullscreen mode

πŸ”° To run that script, hit below command, see the output

node copy-mailbox.js
Enter fullscreen mode Exit fullscreen mode

We’ve done it! you’ve learned major components of IMAP. We connected to an IMAP server, logged in to our email account, listed folders on the remote server, and downloaded message data, create a label, list of mail count. This is exactly what happens under the hood in email apps as you browse your mailbox.

πŸ“— Bonus Section

πŸ”° Read Unseen email and update flag.

If, we like to parse email, use Mailparser. which is an advanced email parser for Node.js. Install package from here [https://nodemailer.com/extras/mailparser/]

npm install mailparser --save
Enter fullscreen mode Exit fullscreen mode
// false = read only mood off, so after fetch we can update flag.
server.openBox('INBOX', false, function (err, box) {
    if (err) throw err;
    server.search(['UNSEEN'], function (err, results) {

        let f = server.fetch(results, {bodies: ''});
        const simpleParser = require('mailparser').simpleParser;

        f.on('message', function (msg, seqno) {
            let uid, headers, body = '';
            msg.on('body', function (stream, info) {
                simpleParser(stream, (err, parsed) => {
                    console.log(parsed.text)
                    console.log(parsed.textAsHtml)
                });
            });
            msg.once('attributes', function (attrs) {
                uid = attrs.uid;
                console.log(attrs);
            });
        });

        f.once("error", function (err) {
            return Promise.reject(err);
        });
    });
});
Enter fullscreen mode Exit fullscreen mode

πŸ‘Œ Congratulations. & Thanks for your time & passion.
Feel free to comments, If you have any issues & queries.

πŸ”° References

Discussion (5)

pic
Editor guide
Collapse
sripalinm profile image
Sripalinm

Hi Sadhan, Initially Thanks a lot for the sharing knowledge, I follow you blog, and I have issue, that
I want to get the gmail body retrieve from simple javascript class, just emai body, I already get it , but can not user that in other class, I man without creating any connection I just want to get email body in other class, can I do it ?

Thanks again,

Collapse
mesadhan profile image
Sadhan Sarker Author

I didn't get your point properly! As far I guess if I not wrong you like to reuse that connection from other classes. I did not try that yet. Let me try first.
Thank you, for such types of motivational comment.

Collapse
mesadhan profile image
Sadhan Sarker Author • Edited

Hope you looking for this.

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

class MailConfig {
    server = new Imap({
        user: 'example@gmail.com',
        password: 'password',
        host: 'imap.gmail.com',
        port: 993,
        tls: true,
        tlsOptions: {
            rejectUnauthorized: false
        },
        authTimeout: 3000
    }).once('error', function (err) {
        console.log('Source Server Error:- ', err);
    });

    constructor() {
        this.server.connect();
    }

    getServer() {
        return this.server;
    }
}

// export & access it from any class

let config = new MailConfig();
let mailServer = config.getServer();

mailServer.once('ready', function () {
    mailServer.openBox('INBOX', true, function (err, box) {
        if (err) throw err;
        console.log('message', 'Source mail server ready');

        let f = mailServer.seq.fetch('1:*', {
            bodies: 'HEADER.FIELDS (FROM TO SUBJECT DATE)',
            struct: true
        });
        f.on('message', function (msg, seqno) {
            console.log('Message #%d', seqno);
            let prefix = '(#' + seqno + ') ';
            msg.on('body', function (stream, info) {
                let buffer = '';
                stream.on('data', function (chunk) {
                    buffer += chunk.toString('utf8');
                });
                stream.once('end', function () {
                    console.log(prefix + 'Parsed header: %s', inspect(Imap.parseHeader(buffer)));
                });
            });
        });

    })
});
Thread Thread
sripalinm profile image
Sripalinm

Hi Sadhan,

Really Thanks for the kind and quick response, Thanks a lot, That's what I asked.

Thread Thread
mesadhan profile image
Sadhan Sarker Author

Sounds Good! If you think I can help, feel free to knock me.