Skip to content
This repository

Add OverrideRecipientSMTP delivery method #423

Closed
wants to merge 2 commits into from

7 participants

Dan Croak Donald Ball Nikolay Shebanov  Brian Cardarella Jake Mallory Mikel Lindsaar Jeremy Kemper
Dan Croak

Use the OverrideRecipientSMTP delivery method when you don't want your program
to accidentally send emails to addresses other than the overridden recipient
which you configure.

An example use case is in your web app's staging environment, your development
team will receive all staging emails without accidentally emailing users with
active email addresses in the database.

Dan Croak and Harlow Ward Add OverrideRecipientSMTP delivery method
Use the OverrideRecipientSMTP delivery method when you don't want your program
to accidentally send emails to addresses other than the overridden recipient
which you configure.

An example use case is in your web app's staging environment, your development
team will receive all staging emails without accidentally emailing users with
active email addresses in the database.
0f9553b
Donald Ball
Collaborator

If you're using ActionMailer, the mail_safe gem does this more flexibly:

https://github.com/myronmarston/mail_safe/

Dan Croak

@dball Thanks for the link. I hadn't seen that one but have seen a number of similar solutions:

https://gist.github.com/138420
https://gist.github.com/2938882
http://blog.smartlogicsolutions.com/2009/04/25/reintroducing-sanitize_email-work-with-production-email-without-fear/
http://henrik.nyh.se/2012/08/abort-mail-delivery-with-rails-3-interceptors/
https://github.com/trampoline/actionmailer_extensions

It's definitely a common need!

It's common enough that I hope it will be accepted into the Mail gem, which I like as the home for this because:

  • it's lower-level than ActionMailer
  • I don't need to install a third-party gem that is more likely to get out of sync with the Mail gem or ActionMailer
  • it's a simple implementation; most of the others involve some dirty monkey-patching
Nikolay Shebanov

+1! I am the one of "the others involve some dirty monkey-patching"

Brian Cardarella

-1 this doesn't seem like something that should go into the core gem but be extracted into a plugin.

If this is a Rails application you should have a specific config/environments/staging.rb and override the smtp settings so that the production mail server is not being used. I see the reason for the use case, but I would prefer to see the Mail gem modified to allow others to hook into it more easily.

Jake Mallory

+1

Dan Croak

Nudge @mikel. Your opinions on this pull request?

Dan Croak croaky referenced this pull request in thoughtbot/suspenders
Closed

Add OverrideRecipientSMTP to staging environment #108

Mikel Lindsaar
Owner

Hi there,

I like this, however, I think we should include the original values some how in the resulting email. ie, setting to, cc and bcc to nil is fine, however, perhaps we should store the original values as X-Override-To, X-Override-Cc and X-Override-Bcc.... that would make for better debugging for the developer as they could open the email and see who the email would have been sent to before it was overridden.

Make this change and appropriate tests and I think we can merge this in.

Dan Croak

Sounds good, @mikel. I've pushed the requested change to 5f01a66. Let me know what you think.

Dan Croak croaky referenced this pull request in thoughtbot/suspenders
Merged

Copy template to lib/override_recipient_smtp.rb #112

Jeremy Kemper
Collaborator

Better yet, leave the message's recipients intact! Check out #477 to set the SMTP envelope recipient directly: mail.smtp_envelope_to = 'catchall-address@example.com'.

Side note: you can do this on your development machine using Postfix, too. In /etc/postfix/main.cf:

recipient_canonical_classes = envelope_recipient
recipient_canonical_maps = regexp:/etc/postfix/recipient_canonical_map

Create /etc/postfix/recipient_canonical_map containing /./ catchall-address@example.com and run postmap /etc/postfix/recipient_canonical_map and postfix reload.

That'll rewrite the recipient on every SMTP envelope to your catchall address.

Jeremy Kemper
Collaborator

Also! This is a better fit for an interceptor than a delivery method:

class CanonicalEnvelopeRecipientInterceptor
  def initialize(recipient)
    @recipient = recipient
  end

  def delivering_email(mail)
    mail.smtp_envelope_to = @recipient
  end
end

Mail.register_interceptor CanonicalEnvelopeRecipientInterceptor.new('catchall-address@example.com')
Jeremy Kemper
Collaborator

Closing in favor of using an interceptor to alter outgoing emails. That can be used with any delivery method, not just SMTP.

Jeremy Kemper jeremy closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 2 unique commits by 3 authors.

Aug 02, 2012
Dan Croak and Harlow Ward Add OverrideRecipientSMTP delivery method
Use the OverrideRecipientSMTP delivery method when you don't want your program
to accidentally send emails to addresses other than the overridden recipient
which you configure.

An example use case is in your web app's staging environment, your development
team will receive all staging emails without accidentally emailing users with
active email addresses in the database.
0f9553b
Nov 17, 2012
Dan Croak croaky Store overriden to, cc, bcc values in X- headers 5f01a66
This page is out of date. Refresh to see the latest.
2  lib/mail/configuration.rb
@@ -31,6 +31,8 @@ def lookup_delivery_method(method)
31 31 Mail::SMTP
32 32 when :smtp
33 33 Mail::SMTP
  34 + when :override_recipient_smtp
  35 + Mail::OverrideRecipientSMTP
34 36 when :sendmail
35 37 Mail::Sendmail
36 38 when :exim
1  lib/mail/network.rb
@@ -2,6 +2,7 @@
2 2
3 3 module Mail
4 4 autoload :SMTP, 'mail/network/delivery_methods/smtp'
  5 + autoload :OverrideRecipientSMTP, 'mail/network/delivery_methods/override_recipient_smtp'
5 6 autoload :FileDelivery, 'mail/network/delivery_methods/file_delivery'
6 7 autoload :Sendmail, 'mail/network/delivery_methods/sendmail'
7 8 autoload :Exim, 'mail/network/delivery_methods/exim'
69 lib/mail/network/delivery_methods/override_recipient_smtp.rb
... ... @@ -0,0 +1,69 @@
  1 +module Mail
  2 + # == Sending Email with Override Recipient SMTP
  3 + #
  4 + # Use the OverrideRecipientSMTP delivery method when you don't want your program
  5 + # to accidentally send emails to addresses other than the overridden recipient
  6 + # which you configure.
  7 + #
  8 + # An example use case is in your web app's staging environment, your development
  9 + # team will receive all staging emails without accidentally emailing users with
  10 + # active email addresses in the database.
  11 + #
  12 + # === Sending via OverrideRecipientSMTP
  13 + #
  14 + # Mail.defaults do
  15 + # delivery_method :override_recipient_smtp, :to => 'staging@example.com'
  16 + # end
  17 + #
  18 + # === Sending to multiple email addresses
  19 + #
  20 + # Mail.defaults do
  21 + # delivery_method :override_recipient_smtp,
  22 + # :to => ['dan@example.com', 'harlow@example.com']
  23 + # end
  24 + #
  25 + # === Debug
  26 + #
  27 + # The original to, cc, and bcc fields are stored in these custom email headers
  28 + # for debugging:
  29 + #
  30 + # X-Override-To
  31 + # X-Override-Cc
  32 + # X-Override-Bcc
  33 + #
  34 + class OverrideRecipientSMTP < Mail::SMTP
  35 + def initialize(values)
  36 + unless values[:to]
  37 + raise ArgumentError.new('A :to option is required when using :override_recipient_smtp')
  38 + end
  39 +
  40 + super(values)
  41 + end
  42 +
  43 + def deliver!(mail)
  44 + store_in_custom_headers(mail)
  45 +
  46 + mail.to = settings[:to]
  47 + mail.cc = nil
  48 + mail.bcc = nil
  49 +
  50 + super(mail)
  51 + end
  52 +
  53 + private
  54 +
  55 + def store_in_custom_headers(mail)
  56 + {
  57 + 'X-Override-To' => mail.to,
  58 + 'X-Override-Cc' => mail.cc,
  59 + 'X-Override-Bcc' => mail.bcc
  60 + }.each do |header, addresses|
  61 + if addresses
  62 + addresses.each do |address|
  63 + mail.header = "#{mail.header}\n#{header}: #{address}"
  64 + end
  65 + end
  66 + end
  67 + end
  68 + end
  69 +end
49 spec/mail/network/delivery_methods/override_recipient_smtp_spec.rb
... ... @@ -0,0 +1,49 @@
  1 +# encoding: utf-8
  2 +require 'spec_helper'
  3 +
  4 +describe 'override recipient SMTP delivery method' do
  5 + it 'raises an error when :to option is missing' do
  6 + doing do
  7 + Mail.defaults do
  8 + delivery_method :override_recipient_smtp
  9 + end
  10 + end.should raise_error(ArgumentError)
  11 + end
  12 +
  13 + it 'supresses email delivery to, cc, and bcc fields' do
  14 + Mail.defaults do
  15 + delivery_method :override_recipient_smtp, :to => 'staging@example.com'
  16 + end
  17 +
  18 + mail = Mail.deliver do
  19 + from 'roger@example.com'
  20 + to 'marcel@example.com'
  21 + cc 'bob@example.com'
  22 + bcc 'dan@example.com'
  23 + end
  24 +
  25 + response = mail.deliver!
  26 +
  27 + response.to.should eq ['staging@example.com']
  28 + response.cc.should eq []
  29 + response.bcc.should eq []
  30 + response.header['X-Override-To'].to_s.should eq '[marcel@example.com, staging@example.com]'
  31 + response.header['X-Override-Cc'].to_s.should eq 'bob@example.com'
  32 + response.header['X-Override-Bcc'].to_s.should eq 'dan@example.com'
  33 + end
  34 +
  35 + it 'can accept an array as configuration' do
  36 + Mail.defaults do
  37 + delivery_method :override_recipient_smtp,
  38 + :to => ['dan@example.com', 'harlow@example.com']
  39 + end
  40 +
  41 + mail = Mail.deliver do
  42 + from 'roger@example.com'
  43 + end
  44 +
  45 + response = mail.deliver!
  46 +
  47 + response.to.should eq ['dan@example.com', 'harlow@example.com']
  48 + end
  49 +end

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.