Class LmtpServer
Parent: Object
LMTP server class. Instances of this class utilize a UNIX socket to implement the LMTP protocol (see RFC 2033) in its most minimal and basic form. LMTP is spoken by possibly any MTA, so using this class you can make your Ruby program an email endpoint as long as you know how to configure your MTA. I’ve only tested with Postfix, though, so no guarantees.
Instances of this class support several callbacks. The main callback is the message callback, which is passed the the block to ::new. It gets called whenever the LMTP client hands in an email, and receives the entire email as plain text as its argument. You can use the “mail” library or other means to parse it. Other callbacks you might find useful can be set with the logging and headers methods.
This class makes no use of threads for multiple connections. Thus, any emails submitted at once to the UNIX domain socket are processed ony-by-one. A single client could block all other clients thus, but because LMTP should only ever be used in a completely trusted environment (see RFC 2033, sections 3 and 5), this is not an issue.
It does employ a mutex for the stop method and the checking of the stopping variable. This means you can safely call stop from another thread.
Example use:
server = LmtpServer.new("/var/spool/postfix/private/mysocket") do |message| puts "--- Start of email ---" puts message puts "--- End of email ---" end server.logging do |level, msg| $stderr.puts "[#{level}] #{msg}" end server.start
The LMTP server by this class implements the following SMTP Service Extensions (see below for the list of RFCs). Do not implement them yourself by utilising moreextensions and the callbacks, they’re there already!
-
PIPELINING
-
ENHANCEDSTATUSCODES
-
8BITMIME
RFCs implemented by this class:
Constants
Name | Value | Description |
---|---|---|
HOSTNAME_FILE | "/etc/hostname".free... |
The machine’s hostname is read from this file. |
VERSION | "0.0.1".freeze |
Version of this library. |
Attributes
- timeout [RW]
Timeout in seconds when a client is forcibly disconnected when it does nothing.
- moreextensions [RW]
This is an array of extra extensions that are announced to the client in response to LHLO. Just append the names of the extensions to this array (e.g. “MYCOOLEXTENSION”). The class will take care to prefix is with the proper LMTP reply code.
This array is empty by default. Modifying it only makes sense if you actually implement the extensions you advertise here.
- successmsg [RW]
Message text to return on a successful message acceptance. This is automatically prefixed by “250 2.6.0 ” so you don’t have to care about the LMTP status code. This text is purely informational and has no meaning to the protocol. It will show up in the sending MTA’s logs.
Public Class Methods
Create a new LMTP server.
Parameters¶ ↑
- path
-
Path on which the UNIX domain socket is created. All parent directories must exist, but the “file” itself must not exist (an ArgumentError is thrown if it exists).
- mode (nil)
-
UNIX permissions to set on the UNIX socket file as a numeric mode (example: 0666 for rw-rw-rw-). User and Group of the file are determined by whatever the process environment mandates.
nil
means to use whatever the process umask mandates. - callback
-
Message callback. Receives any email as a string that is passed to this LMTP server. The string will contain the original carriagereturn+newline line breaks from the protcol.
Return value¶ ↑
Returns the new instance.
Public Instance Methods
Override the callback used for responding to the LMTP client for the LMTP commands before DATA, e.g. MAIL FROM and RCPT TO. The callback receives the entire line the client sent, including the trailing carriagereturn-newline.
The default callback only answers “250 2.1.0 ok” for every command. Note that RFC 2033 (LMTP) requires in section 5 that any LMTP server MUST implement RFC 2034, which in turn refers to RFC 1893 for the actual status codes, so for any replies you make you must make use of the extended statuscodes defined in RFC 1893 in the format defined by RFC 2034. Don’t worry — both of these RFCs are simple enough to just read quickly through them.
Example:
server.headers do |line| case line when /^MAIL FROM/ then "250 ok" when /^RCPT TO:<.*?>/ then if this_account_exists($1) "250 2.1.5 Recipient ok" else "550 5.1.1 Recipient does not exist over here." end else "250 2.1.0 ok" end end
Note that the replies you define here are not immediately sent to the client, which is a result of the PIPELINING extension that is required by LMTP (see RFC 2033, section 5, and RFC 1854). Instead they’re accumulated and send as a big swall to the client when he issues the DATA command.
Specify the logging callback. It will receive a syslog logging level as a symbol and the log message.
By default, no logging callback is set and hence nothing is logged.
Create the UNIX domain socket and start listening on it. This method starts a listening loop and thus blocks. Use stop from another thread to issue a halt.
Halt the running server. This method is threadsafe.