The @mail module contains functions for sending and receiving electronic mail (email) using Simple Mail Transfer Protocol (SMTP).

@mail Functions

:send(mail,password,host,port,mode) #

Send mail via host.

Parameters

Return

Notes

The mail object should contain the following fields:

{ host: string (optional, default is 'localhost'), from: string, to: string, cc: string (optional), bcc: string (optional), user: string (optional), sender: string, recipients: array of strings, subject: string, message: string, attachments: object of filename to data (optional) }

The "host" field is the host of the client, used to identify itself to the server. Most servers (like gmail) require that this field be set, so it will default to "localhost" if left unspecified.

The from/to/cc/bcc are header fields, and can contain wrapped email addresses, like '"Alice Bob" <alicebob@example.com>', as well as bare email addresses like 'alicebob@example.com'. The to/cc/bcc fields can contain multiple email addresses separated by commas. Additionally, header fields are not canonical, meaning that they do not necessarily have to match the sender/recipient fields. (This happens sometimes with newsletters—the "to" field doesn't match the actual recipient.) The sender/recipient fields are canonical and should only contain bare email addresses, which can be extracted from the header fields with :extract_addresses().

The "message" field should contain the message as HTML. Attached images can be embedded in the message by prepending "cid:". For example, if "attachments" contains "test.jpg", the message can contain <img src="cid:test.png"/> to display the image inline.

If password is not blank, PLAIN authentication is attempted, in combination with the "user" field in the mail object.

The port defaults to 465, which is the de-facto standard port for SMTPS. Port 25 is the standard port for plaintext, and port 587 is the standard port for STARTTLS. The Axiom mail server defaults to port 2525 for local testing.

Example

o = { from: '"Alice" <alice@example.com>', to: 'Bob <bob@example.com>, other@example.com', subject: 'whatever' } o.sender = mail.extract_addresses(o.from)[0] o.recipients = mail.extract_addresses(o.to) o.message = ` <p>Dear everyone,</p> <p>Here is mail.</p> <p>-Alice</p> ` print o mail.send(o,'','localhost',2525,0) #send to local test server #send with login o.user = 'alice@example.com' print o mail.send(o,'abc123','localhost',2525,0) #send to local test server

This is an example of how to send mail with gmail. Note that:

o = { from: '"Your Name" <yourname@gmail.com>', to: '"Your Friend" <yourfriend@example.com>', subject: 'Axiom mail test' } o.user = o.sender = mail.extract_addresses(o.from)[0] o.recipients = mail.extract_addresses(o.to) o.message = ` <p>Dear Friend,</p> <p>This is a test.</p> <p>-Name</p> ` print o mail.send(o,'yourpassword','smtp.gmail.com') #use port 465/SMTPS by default :extract_addresses(field) #

Extract email addresses from header field.

Parameters

Return

Notes

Can be used to set the "sender" and "recipient" fields of the mail object for :send().

Example

mail.extract_addresses('you@there.com, "Bob" <bob@example.com>') #[ "you@there.com", "bob@example.com" ]

@server Functions

:use(on_mail,on_rcpt,on_auth) #

Set global mail server event handlers.

Parameters

Notes

The on_mail handler receives a mail object like:

{ ip: string, host: string, from: string, to: string, cc: string (optional), bcc: string (optional), user: string (optional), sender: string, recipients: array of strings, subject: string, message: string, attachments: object of filename to data (optional), original: string }

This is similar to the object taken by :send(), except it contains two extra fields, "ip" and "original". The "ip" field can be used to validate the "host" field. (One could do a DNS lookup on the host to see if it matches the ip.) The "original" field is the raw data received, which is used to derive the "message" and "attachment" fields. As stated in the :send() notes, from/to/cc/bcc fields do not necessarily match the sender/recipient fields. The sender/recipient fields are canonical and contain bare email addresses, whereas the from/to/cc/bcc fields are not canonical and can contain both bare or wrapped email addresses. The "user" field is the field used for authentication. If no authentication occurred, the "user" field is blank. See the following paragraph about the on_auth handler for more details. A typical server should check that from/user/sender fields all match. (The bare email should be extracted from the "from" field with :extract_addresses(). The "user" field should be checked if not blank.)

The on_rcpt handler is called before on_mail on every recipient, and receives a single string, which is the bare email address of the recipient. The handler should return true if it can receive mail for the recipient, or false otherwise. If the handler returns true, the recipient is added to the "recipients" array to the mail object received in on_mail. If on_rcpt is not set, the default handler just accepts all recipients.

The on_auth handler is called when the client attempts to authenticate, and receives two strings, username and password. The handler should return true if the username and password are valid, or false otherwise. If the handler returns true, the username is set as the "user" field in the mail object received in on_mail. If on_auth is not set, the default handler just accepts all credentials.

Example

#simple example demonstrating all handlers :on_mail(m) #warn if not authenticated if !m.user || m.user!=m.sender print 'Not logged in and/or sender mismatch:',m.user.json(),'!=',m.sender.json() print 'Received mail:',m :on_rcpt(recipient) print 'Recipient:',recipient host = recipient.split('@')[1] if host=='example.com' return true return false :on_auth(username,password) print 'Login:',username,password if username=='alice' && password=='abc123' return true return false mail.server.use(on_mail,on_rcpt,on_auth) mail.server.run() :run(config) #

Run the global mail server using the specified configuration

Parameters

Notes

The default config file (mail.ini) looks like this:

;SMTP port. If tls_crt/tls_key are specified, STARTTLS will also be supported. Comment out to disable port = 2525 ;Whether or not to bind to localhost only (vs binding to all adapters) local = true ;Comment these in to enable TLS/SSL (SMTPS) ;tls_port = 465 ;tls_crt = server.crt ;tls_key = server.key

Instead of a config file, an object can be passed containing all the necessary variables. At a bare minimum, the "port" variable is required.

This function blocks until the global mail server is shut down (by ctrl-c or process signal/kill). This function should only be called from the main thread. Consider using background threads or processes if background processing is needed.

Example

#create a file called mail.ini if not exist, and use those values #use all default handlers, which will just print any mail received, and accept all recipients/authentication mail.server.run()