mail_room is a configuration based process that will listen for incoming e-mail and execute a delivery method when a new message is received. mail_room supports the following methods for receiving e-mail:
Examples of delivery methods include:
Add this line to your application's Gemfile:
gem 'mail_room'
And then execute:
$ bundle
Or install it yourself as:
$ gem install mail_room
You will also need to install faraday
or letter_opener
if you use the postback
or letter_opener
delivery methods, respectively.
mail_room -c /path/to/config.yml
Note: To ignore missing config file or missing mailboxes
key, use -q
or --quiet
---
:mailboxes:
-
:email: "user1@gmail.com"
:password: "password"
:name: "inbox"
:search_command: 'NEW'
:logger:
:log_path: /path/to/logfile/for/mailroom
:delivery_options:
:delivery_url: "http://localhost:3000/inbox"
:delivery_token: "abcdefg"
:content_type: "text/plain"
-
:email: "user2@gmail.com"
:password: "password"
:name: "inbox"
:delivery_method: postback
:delivery_options:
:delivery_url: "http://localhost:3000/inbox"
:delivery_token: "abcdefg"
-
:email: "user3@gmail.com"
:password: "password"
:name: "inbox"
:delivery_method: logger
:delivery_options:
:log_path: "/var/log/user3-email.log"
-
:email: "user4@gmail.com"
:password: "password"
:name: "inbox"
:delivery_method: letter_opener
:delete_after_delivery: true
:expunge_deleted: true
:delivery_options:
:location: "/tmp/user4-email"
-
:email: "user5@gmail.com"
:password: "password"
:name: "inbox"
:delivery_method: sidekiq
:delivery_options:
:redis_url: redis://localhost:6379
:worker: EmailReceiverWorker
-
:email: "user6@gmail.com"
:password: "password"
:name: "inbox"
:delivery_method: sidekiq
:delivery_options:
# When pointing to sentinel, follow this sintax for redis URLs:
# redis://:<password>@<master-name>/
:redis_url: redis://:password@my-redis-sentinel/
:sentinels:
-
:host: 127.0.0.1
:port: 26379
:worker: EmailReceiverWorker
-
:email: "user7@outlook365.com"
:password: "password"
:name: "inbox"
:inbox_method: microsoft_graph
:inbox_options:
:tenant_id: 12345
:client_id: ABCDE
:client_secret: YOUR-SECRET-HERE
:poll_interval: 60
:azure_ad_endpoint: https://login.microsoftonline.com
:graph_endpoint: https://graph.microsoft.com
:delivery_method: sidekiq
:delivery_options:
:redis_url: redis://localhost:6379
:worker: EmailReceiverWorker
-
:email: "user8@gmail.com"
:password: "password"
:name: "inbox"
:delivery_method: postback
:delivery_options:
:delivery_url: "http://localhost:3000/inbox"
:jwt_auth_header: "Mailroom-Api-Request"
:jwt_issuer: "mailroom"
:jwt_algorithm: "HS256"
:jwt_secret_path: "/etc/secrets/mailroom/.mailroom_secret"
Note: If using delete_after_delivery
, you also probably want to use
expunge_deleted
unless you really know what you're doing.
By default, IMAP mode is assumed for reading a mailbox.
You can set per-mailbox configuration for the IMAP server's host
(default: 'imap.gmail.com'), port
(default: 993), ssl
(default: true), and start_tls
(default: false).
If you want to set additional options for IMAP SSL you can pass a YAML hash to match SSLContext#set_params. If you set verify_mode
to :none
it'll replace with the appropriate constant.
If you're seeing the error Please log in via your web browser: https://support.google.com/mail/accounts/answer/78754 (Failure)
, you need to configure your Gmail account to allow less secure apps to access it: https://support.google.com/accounts/answer/6010255.
To use the Microsoft Graph API instead of IMAP to read e-mail, you will need to create an application in Microsoft Entra ID (formerly known as Azure Active Directory). See the Microsoft instructions for more details:
Microsoft Entra ID
.Manage
, select App registrations
> New registration
.Name
for your application, such as MailRoom
. Users of your app might see this name, and you can change it later.Supported account types
is listed, select the appropriate option.Redirect URI
blank. This is not needed.Register
.Manage
, select Certificates & secrets
.Client secrets
, select New client secret
, and enter a name.Expires
, select Never
, unless you plan on updating the credentials every time it expires.Add
. Record the secret value in a safe location for use in a later step.Manage
, select API Permissions
> Add a permission
. Select Microsoft Graph
.Application permissions
.Mail
node, select Mail.ReadWrite
, and then select Add permissions.User.Read
is listed in the permission list, you can delete this.Grant admin consent
for these permissions.Note that for MailRoom to work as a service account, this application
must have the Mail.ReadWrite
to read/write mail in all
mailboxes. However, while this appears to be security risk,
we can configure an application access policy to limit the
mailbox access for this account. Follow these instructions
to setup PowerShell and configure this policy.
In the MailRoom configuration, set inbox_method
to microsoft_graph
.
You will also need:
Overview
section in the Azure app pageFill in inbox_options
with these values:
:inbox_method: microsoft_graph
:inbox_options:
:tenant_id: 12345
:client_id: ABCDE
:client_secret: YOUR-SECRET-HERE
:poll_interval: 60
By default, MailRoom will poll for new messages every 60 seconds. poll_interval
configures the number of
seconds to poll. Setting the value to 0 or under will default to 60 seconds.
MailRoom will default to using the standard Azure HTTPS endpoints. To
configure MailRoom with Microsoft Cloud for US Government or other
national cloud deployments, set
the azure_ad_endpoint
and graph_endpoint
accordingly. For example,
for Microsoft Cloud for US Government:
:inbox_method: microsoft_graph
:inbox_options:
:tenant_id: 12345
:client_id: ABCDE
:client_secret: YOUR-SECRET-HERE
:poll_interval: 60
:azure_ad_endpoint: https://login.microsoftonline.us
:graph_endpoint: https://graph.microsoft.us
Requires faraday
gem be installed.
NOTE: If you're using Ruby >= 2.0
, you'll need to use Faraday from >= 0.8.9
. Versions before this seem to have some weird behavior with mail_room
.
The default delivery method, requires delivery_url
and delivery_token
in
configuration.
You can pass content_type:
option to overwrite faraday's
default content-type(application/x-www-form-urlencoded
) for post requests, we recommend passing text/plain
as content-type.
As the postback is essentially using your app as if it were an API endpoint, you may need to disable forgery protection as you would with a JSON API.
Deliver the message by pushing it onto the configured Sidekiq queue to be handled by a custom worker.
Requires redis
gem to be installed.
Configured with :delivery_method: sidekiq
.
Delivery options:
redis://localhost:6379
.default
.An example worker implementation looks like this:
class EmailReceiverWorker
include Sidekiq::Worker
def perform(message)
mail = Mail::Message.new(message)
puts "New mail from #{mail.from.first}: #{mail.subject}"
end
end
Deliver the message by pushing it onto the configured Que queue to be handled by a custom worker.
Requires pg
gem to be installed.
Configured with :delivery_method: que
.
Delivery options:
localhost
.5432
.default
.100
, lowest Que default priority.An example worker implementation looks like this:
class EmailReceiverJob < Que::Job
def run(message)
mail = Mail::Message.new(message)
puts "New mail from #{mail.from.first}: #{mail.subject}"
end
end
Configured with :delivery_method: logger
.
If the :log_path:
delivery option is not provided, defaults to STDOUT
Configured with :delivery_method: noop
.
Does nothing, like it says.
Requires letter_opener
gem be installed.
Configured with :delivery_method: letter_opener
.
Uses Ryan Bates' excellent letter_opener gem.
MailRoom can deliver mail to Rails using the ActionMailbox configuration options for an SMTP relay.
In summary (from the ActionMailbox docs)
Configure Rails to use the :relay
ingress option:
# config/environments/production.rb
config.action_mailbox.ingress = :relay
Generate a strong password (e.g., using SecureRandom or something) and add it to Rails config:
using rails credentials:edit
under action_mailbox.ingress_password
.
And finally, configure MailRoom to use the postback configuration with the options:
:delivery_method: postback
:delivery_options:
:delivery_url: https://example.com/rails/action_mailbox/relay/inbound_emails
:username: actionmailbox
:password: <INGRESS_PASSWORD>
postback
in RailsIf you have a controller that you're sending to, with forgery protection
disabled, you can get the raw string of the email using request.body.read
.
I would recommend having the mail
gem bundled and parse the email using
Mail.read_from_string(request.body.read)
.
Note: If you get the exception (Rack::QueryParser::InvalidParameterError (invalid %-encoding...
)
it's probably because the content-type is set to Faraday's default, which is HEADERS['content-type'] = 'application/x-www-form-urlencoded'
. It can cause Rack
to crash due to InvalidParameterError
exception. When you send a post with application/x-www-form-urlencoded
, Rack
will attempt to parse the input and can end up raising an exception, for example if the email that you are forwarding contain %%
in its content or headers it will cause Rack to crash with the message above.
By default, the IDLE command will wait for 29 minutes (in order to keep the server connection happy).
If you'd prefer not to wait that long, you can pass idle_timeout
in seconds for your mailbox configuration.
This setting allows configuration of the IMAP search command sent to the server. This still defaults 'UNSEEN'. You may find that 'NEW' works better for you.
I suggest running with either upstart or init.d. Check out this wiki page for some example scripts for both: https://github.com/tpitale/mail_room/wiki/Init-Scripts-for-Running-mail_room
When running multiple instances of MailRoom against a single mailbox, to try to prevent delivery of the same message multiple times, we can configure Arbitration using Redis.
:mailboxes:
-
:email: "user1@gmail.com"
:password: "password"
:name: "inbox"
:delivery_method: postback
:delivery_options:
:delivery_url: "http://localhost:3000/inbox"
:delivery_token: "abcdefg"
:arbitration_method: redis
:arbitration_options:
# The Redis server to connect with. Defaults to redis://localhost:6379.
:redis_url: redis://redis.example.com:6379
# [DEPRECATED] The Redis namespace to house the Redis keys under. Optional.
:namespace: mail_room
-
:email: "user2@gmail.com"
:password: "password"
:name: "inbox"
:delivery_method: postback
:delivery_options:
:delivery_url: "http://localhost:3000/inbox"
:delivery_token: "abcdefg"
:arbitration_method: redis
:arbitration_options:
# When pointing to sentinel, follow this sintax for redis URLs:
# redis://:<password>@<master-name>/
:redis_url: redis://:password@my-redis-sentinel/
:sentinels:
-
:host: 127.0.0.1
:port: 26379
# [DEPRECATED] The Redis namespace to house the Redis keys under. Optional.
:namespace: mail_room
Note: This will likely never be a perfect system for preventing multiple deliveries of the same message, so I would advise checking the unique message_id
if you are running in this situation.
Note: There are other scenarios for preventing duplication of messages at scale that may be more appropriate in your particular setup. One such example is using multiple inboxes in reply-by-email situations. Another is to use labels and configure a different SEARCH
command for each instance of MailRoom.
Redis Sentinel provides high availability for Redis. Please read their documentation first, before enabling it with mail_room.
To connect to a Sentinel, you need to setup authentication to both sentinels and redis daemons first, and make sure both are binding to a reachable IP address.
In mail_room, when you are connecting to a Sentinel, you have to inform the master-name
and the password
through
redis_url
param, following this syntax:
redis://:<password>@<master-name>/
You also have to inform at least one pair of host
and port
for a sentinel in your cluster.
To have a minimum reliable setup, you need at least 3
sentinel nodes and 3
redis servers (1 master, 2 slaves).
MailRoom will output JSON-formatted logs to give some observability into its operations.
Simply configure a log_path
for the logger
on any of your mailboxes. By default, nothing will be logged.
If you wish to log to STDOUT
or STDERR
instead of a file, you can pass :stdout
or :stderr
,
respectively and MailRoom will log there.
git checkout -b my-new-feature
)git commit -am 'Add some feature'
)git push origin my-new-feature
)