How to do it...

Let's start by requiring the relevant dependencies:

const fs = require('fs') 
const path = require('path')
const smtp = require('smtp-protocol')

We're only going to accept emails to certain host domains, and only for certain users. We also need somewhere to store the emails we have accepted.

So let's create two whitelist sets, one for hosts and the other for users, along with a target path for mailboxes:

const hosts = new Set(['localhost', 'example.com']) 
const users = new Set(['you', 'another'])
const mailDir = path.join(__dirname, 'mail')

Before we create the server, we have some setup to perform. We need to make sure that the mail directory and user mailboxes exist on the filesystem. If they don't, we need to create them.

Let's write the following code to do that:

function ensureDir (dir, cb) { 
try { fs.mkdirSync(dir) } catch (e) {
if (e.code !== 'EEXIST') throw e
}
}

ensureDir(mailDir)
for (let user of users) ensureDir(path.join(mailDir, user))

Next we'll create out SMTP server:

const server = smtp.createServer((req) => { 
req.on('to', filter)
req.on('message', (stream, ack) => save(req, stream, ack))
req.on('error', (err) => console.error(err))
})

server.listen(2525)

This gives us an outline of what we'll do for each incoming SMTP request.

We still need to implement the filter and save functions shortly.

Our filter function will deconstruct the intended recipient email address and check the hosts and users whitelist, calling accept or reject accordingly:

function filter (to, {accept, reject}) { 
const [user, host] = to.split('@')
if (hosts.has(host) && users.has(user)) {
accept()
return
}
reject(550, 'mailbox not available')
}

Finally, our save function will take the incoming message and save it to any relevant mailboxes:

function save (req, stream, {accept}) { 
const {from, to} = req
accept()
to.forEach((rcpt) => {
const [user] = rcpt.split('@')
const dest = path.join(mailDir, user, `${from}-${Date.now()}`)
const mail = fs.createWriteStream(dest)
mail.write(`From: ${from} \n`)
mail.write(`To: ${rcpt} \n\n`)
stream.pipe(mail, {end: false})
})
}

Now if we run our index.js file, our server should start (and create the relevant directory structures):

$ node index.js

We can manually test our mail server by opening a new terminal and running:

$ node -e "process.stdin.pipe(require('net')
.connect(2525)).pipe(process.stdout)"

This will allow us to interact with our SMTP server in real time, which means we can manually create an email message at the protocol level.

If we use the following script, we should be able to construct a message to our SMTP server:

helo 
mail from: me@me.com
rcpt to: you@example.com
data
hello there!
.
quit

After each line, we should receive a response from our server. The whole interaction should look similar to the following screenshot: