Class LmtpServer

Parent: Object

Method List

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!

RFCs implemented by this class:

Constants

NameValueDescription
HOSTNAME_FILE"/etc/hostname".free...

The machine’s hostname is read from this file.

VERSION"0.0.1".freeze

Version of this library.

Attributes

Public Class Methods

new(path, mode = nil, &callback)

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.

[Show source]

Public Instance Methods

headers(&callback)

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.

[Show source]

logging(&callback)

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.

[Show source]

start()

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.

[Show source]

stop()

Halt the running server. This method is threadsafe.

[Show source]