📜 ⬆️ ⬇️

We accept email on Node.js

E-mail as www appeared at the dawn of the Internet, and despite its archaic nature continues to hold the position of one of the main technologies of the network. In the meantime, developers do not value it too much and use it unilaterally, indicating by the sender noreply. And first of all it is connected with the laboriousness of the process of processing incoming correspondence.


Meanwhile, the praise of the community Node.js, packages have appeared that allow mail to be received without pain and suffering - this is smtp-server and mailparser . Let me show you how in a couple of dozen lines of code to create your mail server with SSL encryption support, spam filtering with the help of spamassassin and other joys.


Receive letters


The smtp-server module is responsible for receiving letters. There is nothing difficult in his work, the only thing that can make you spend a few hours of time is the TLS setting, which is not too obvious (I’ll tell you about it later).


const fs = require('fs'); const {SMTPServer} = require('smtp-server'); const smtp = new SMTPServer({ secure: false, key: fs.readFileSync('./key.pem'), cert: fs.readFileSync('./cert.pem'), onRcptTo, onData, authOptional: true, }); //  .      . function onRcptTo({address}, session, callback) { if (address.starts('noreply@')) { callback(new Error(`Address ${address} is not allowed receiver`)); } else { callback(); } } //    function onData(stream, session, callback) { // Stream –    . Callback    . //       . callback(); } 

Settings


In general, the settings are somewhat larger, but I will describe the main ones that we will need to implement a small server.


secure


The fact is that encryption can be used in two ways: when establishing a connection ( secure: true ) or switching to an encrypted stream using the STARTTLS header ( secure: false ). If you are listening to port 25, set it to false , 587th (465th) is true . To decide on a port, I advise you to read the mailgun article about the history of ports assigned to mail protocols.


key, cert


SSL key and certificate. By default, smtp-server uses its own self-signed certificate, but I would not advise it to be used when we have Let's Encrypt.


onRcptTo


If no address has been approved in the onRcptTo method - onData will not be called. For each letter a report will be generated on the sender’s side. Yandex generates this:


 This is the mail system at host yandex.ru. I'm sorry to have to inform you that your message could not be delivered to one or more recipients. It's attached below. Please, do not reply to this message. <noreply@hm.rumk.in>: host hm.rumk.in[159.203.137.17] said: 550 Mailbox noreply@hm.rumk.in could not receive messages (in reply to RCPT TO command) 

onMailFrom


This setting allows you to assign a handler for the sender's address to filter by senders.


onData


Everything is simple, the main thing is to callback to avoid memory leaks.


authOptional


Allows you to receive mail from an unauthorized sender, such as Yandex or Gmail.


logger


May be true or an instance of the logger supporting interface bunyan.


Parsing


Everything is easier with parsing. You need to connect the parser for mailparser letters:


 const {MailParser} = require('mailparser'); 

and modify the onData function:


 function onData(stream, session, callback) { const parser = new MailParser(); stream.pipe(parser); parser.on('error', callback); parser.on('end', (mail) => { // Process mail body... callback(); }); } 

As a result of the parsing, you will receive an object of the following form:


 { "html": "<div>Hi this is a test message. Notify me if you get it</div>\n", "headers": { "received": [ "from mxback5g.mail.yandex.net (mxback5g.mail.yandex.net [77.88.29.166]) by forward17p.cmail.yandex.net (Yandex) with ESMTP id 372CD212FE for <c28ec25d@hm.rumk.in>; Sat, 5 Nov 2016 06:22:23 +0300 (MSK)", "from web20g.yandex.ru (web20g.yandex.ru [95.108.253.229]) by mxback5g.mail.yandex.net (nwsmtp/Yandex) with ESMTP id j2CjR0Q3Ek-MN2SfLo3; Sat, 05 Nov 2016 06:22:23 +0300", "by web20g.yandex.ru with HTTP; Sat, 05 Nov 2016 06:22:23 +0300" ], "from": "Some User <user@host>", "to": "c28ec25d@hm.rumk.in", "subject": "asdasd a", "mime-version": "1.0", "message-id": "<7119991478316143@web20g.yandex.ru>", "x-mailer": "Yamail [ http://yandex.ru ] 5.0", "date": "Sat, 05 Nov 2016 06:22:23 +0300", "content-transfer-encoding": "7bit", "content-type": "text/html" }, "subject": "Test message", "messageId": "7119991478316143@web20g.yandex.ru", "priority": "normal", "from": [ { "address": "user@host", "name": "Some User" } ], "to": [ { "address": "c28ec25d@hm.rumk.in", "name": "" } ], "date": "2016-11-05T03:22:23.000Z", "receivedDate": "2016-11-05T03:22:23.000Z" } 

We can also connect the spamassassin module to calculate the spam index of spamScore . To do this, you need to install spamassassin and the spamc-stream module. It is as easy to use as mailparser.


To do this, you need to install and run spamassassin:


 # Debian/Ubuntu $ sudo apt-get install spamassassin # Fedora/CentOS $ sudo yum install spamassassin 

Spamassassin contains a set of rules, each of which is applied to the letter, and if the rule worked, then the index increases. When the index exceeds the allowed value (usually 5), the letter is recognized as spam. For example, the index will increase if the letter contains only the html-version without text. Spamassassin is a server to which a letter is sent for analysis. Smapc is a client for smapassassin. We will redirect the letter first to spamassassin, and then to the parser.


 const SpamcStream = require('spamc-stream'); const spamc = new SpamcStream(); //   onData(stream, session, callback) { const reporter = spamc.report(); let report; const parser = new MailParser(); stream.pipe(reporter).pipe(mailparser); reporter.on('report', (result) => { report = result; }); parser.on('end', (mail) => { if (report.isSpam) { // Save mail into spam directory } else { // Process mail body... } callback(); }); reporter.on('error', callback); parser.on('error', callback); } 

It should also be noted that the parser of letters can create streams from attachments, which allows you to conveniently and efficiently redirect them to BLOB storage, well, or just write to disk.


Note


If you decide to accept mail from an unlimited number of senders, you will need to implement support for SPF verification and, preferably, DKIM. But this is material for a separate article.


Example


See how it works on the test page . Sending a letter to a temporary e-mail, you will see a JSON structure ready for further processing. Messages are delivered in real time via WebSocket. The source code of the example itself is posted in the rumkin / hypemail repository .


The author of the server and parser is Andris Reinman. And support the projects with commits.


')

Source: https://habr.com/ru/post/315134/


All Articles