Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose & override the SMTP envelope #477

Merged
merged 1 commit into from
May 14, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 7 additions & 9 deletions lib/mail/check_delivery_params.rb
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
module Mail
module CheckDeliveryParams
def check_delivery_params(mail)
envelope_from = mail.return_path || mail.sender || mail.from_addrs.first
if envelope_from.blank?
raise ArgumentError.new('A sender (Return-Path, Sender or From) required to send a message')
if mail.smtp_envelope_from.blank?
raise ArgumentError.new('An SMTP From address is required to send a message. Set the message smtp_envelope_from, return_path, sender, or from address.')
end

destinations ||= mail.destinations if mail.respond_to?(:destinations) && mail.destinations
if destinations.blank?
raise ArgumentError.new('At least one recipient (To, Cc or Bcc) is required to send a message')
if mail.smtp_envelope_to.blank?
raise ArgumentError.new('An SMTP To address is required to send a message. Set the message smtp_envelope_to, to, cc, or bcc address.')
end

message ||= mail.encoded if mail.respond_to?(:encoded)
message = mail.encoded if mail.respond_to?(:encoded)
if message.blank?
raise ArgumentError.new('A encoded content is required to send a message')
raise ArgumentError.new('An encoded message is required to send an email')
end

[envelope_from, destinations, message]
[mail.smtp_envelope_from, mail.smtp_envelope_to, message]
end
end
end
79 changes: 79 additions & 0 deletions lib/mail/message.rb
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ def initialize(*args, &block)
@charset = 'UTF-8'
@defaulted_charset = true

@smtp_envelope_from = nil
@smtp_envelope_to = nil

@perform_deliveries = true
@raise_delivery_errors = true

Expand Down Expand Up @@ -1023,6 +1026,82 @@ def sender=( val )
header[:sender] = val
end

# Returns the SMTP Envelope From value of the mail object, as a single
# string of an address spec.
#
# Defaults to Return-Path, Sender, or the first From address.
#
# Example:
#
# mail.smtp_envelope_from = 'Mikel <[email protected]>'
# mail.smtp_envelope_from #=> '[email protected]'
#
# Also allows you to set the value by passing a value as a parameter
#
# Example:
#
# mail.smtp_envelope_from 'Mikel <[email protected]>'
# mail.smtp_envelope_from #=> '[email protected]'
def smtp_envelope_from( val = nil )
if val
self.smtp_envelope_from = val
else
@smtp_envelope_from || return_path || sender || from_addrs.first
end
end

# Sets the From address on the SMTP Envelope.
#
# Example:
#
# mail.smtp_envelope_from = 'Mikel <[email protected]>'
# mail.smtp_envelope_from #=> '[email protected]'
def smtp_envelope_from=( val )
@smtp_envelope_from = val
end

# Returns the SMTP Envelope To value of the mail object.
#
# Defaults to #destinations: To, Cc, and Bcc addresses.
#
# Example:
#
# mail.smtp_envelope_to = 'Mikel <[email protected]>'
# mail.smtp_envelope_to #=> '[email protected]'
#
# Also allows you to set the value by passing a value as a parameter
#
# Example:
#
# mail.smtp_envelope_to ['Mikel <[email protected]>', 'Lindsaar <[email protected]>']
# mail.smtp_envelope_to #=> ['[email protected]', '[email protected]']
def smtp_envelope_to( val = nil )
if val
self.smtp_envelope_to = val
else
@smtp_envelope_to || destinations
end
end

# Sets the To addresses on the SMTP Envelope.
#
# Example:
#
# mail.smtp_envelope_to = 'Mikel <[email protected]>'
# mail.smtp_envelope_to #=> '[email protected]'
#
# mail.smtp_envelope_to = ['Mikel <[email protected]>', 'Lindsaar <[email protected]>']
# mail.smtp_envelope_to #=> ['[email protected]', '[email protected]']
def smtp_envelope_to=( val )
@smtp_envelope_to =
case val
when Array, NilClass
val
else
[val]
end
end

# Returns the decoded value of the subject field, as a single string.
#
# Example:
Expand Down
18 changes: 8 additions & 10 deletions lib/mail/network/delivery_methods/sendmail.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,26 +41,24 @@ class Sendmail

def initialize(values)
self.settings = { :location => '/usr/sbin/sendmail',
:arguments => '-i -t' }.merge(values)
:arguments => '-i' }.merge(values)
end

attr_accessor :settings

def deliver!(mail)
check_delivery_params(mail)
smtp_from, smtp_to, message = check_delivery_params(mail)

envelope_from = mail.return_path || mail.sender || mail.from_addrs.first
return_path = "-f #{self.class.shellquote(envelope_from)}" if envelope_from
from = "-f #{self.class.shellquote(smtp_from)}"
to = smtp_to.map { |to| self.class.shellquote(to) }.join(' ')

arguments = [settings[:arguments], return_path, '--'].compact.join(" ")

quoted_destinations = mail.destinations.collect { |d| self.class.shellquote(d) }
self.class.call(settings[:location], arguments, quoted_destinations.join(' '), mail)
arguments = "#{settings[:arguments]} #{from} --"
self.class.call(settings[:location], arguments, to, message)
end

def self.call(path, arguments, destinations, mail)
def self.call(path, arguments, destinations, encoded_message)
popen "#{path} #{arguments} #{destinations}" do |io|
io.puts mail.encoded.to_lf
io.puts encoded_message.to_lf
io.flush
end
end
Expand Down
10 changes: 5 additions & 5 deletions lib/mail/network/delivery_methods/smtp.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,13 @@ def initialize(values)
:tls => nil
}.merge!(values)
end

attr_accessor :settings

# Send the message via SMTP.
# The from and to attributes are optional. If not set, they are retrieve from the Message.
def deliver!(mail)
envelope_from, destinations, message = check_delivery_params(mail)
smtp_from, smtp_to, message = check_delivery_params(mail)

smtp = Net::SMTP.new(settings[:address], settings[:port])
if settings[:tls] || settings[:ssl]
Expand All @@ -107,10 +107,10 @@ def deliver!(mail)
smtp.enable_starttls_auto(ssl_context)
end
end

response = nil
smtp.start(settings[:domain], settings[:user_name], settings[:password], settings[:authentication]) do |smtp_obj|
response = smtp_obj.sendmail(message, envelope_from, destinations)
response = smtp_obj.sendmail(message, smtp_from, smtp_to)
end

if settings[:return_response]
Expand Down
12 changes: 6 additions & 6 deletions lib/mail/network/delivery_methods/smtp_connection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,18 @@ def initialize(values)
self.smtp = values[:connection]
self.settings = values
end

attr_accessor :smtp
attr_accessor :settings

# Send the message via SMTP.
# The from and to attributes are optional. If not set, they are retrieve from the Message.
def deliver!(mail)
envelope_from, destinations, message = check_delivery_params(mail)
response = smtp.sendmail(message, envelope_from, destinations)
smtp_from, smtp_to, message = check_delivery_params(mail)
response = smtp.sendmail(message, smtp_from, smtp_to)

settings[:return_response] ? response : self
settings[:return_response] ? response : self
end

end
end
62 changes: 62 additions & 0 deletions spec/mail/message_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1731,4 +1731,66 @@ def self.delivering_email(mail)

end

describe 'SMTP envelope From' do
it 'should respond' do
Mail::Message.new.should respond_to(:smtp_envelope_from)
end

it 'should default to return_path, sender, or first from address' do
message = Mail::Message.new do
return_path 'return'
sender 'sender'
from 'from'
end
message.smtp_envelope_from.should eq 'return'

message.return_path = nil
message.smtp_envelope_from.should eq 'sender'

message.sender = nil
message.smtp_envelope_from.should eq 'from'
end

it 'can be overridden' do
message = Mail::Message.new { return_path 'return' }

message.smtp_envelope_from = 'envelope_from'
message.smtp_envelope_from.should eq 'envelope_from'

message.smtp_envelope_from = 'declared_from'
message.smtp_envelope_from.should eq 'declared_from'

message.smtp_envelope_from = nil
message.smtp_envelope_from.should eq 'return'
end
end

describe 'SMTP envelope To' do
it 'should respond' do
Mail::Message.new.should respond_to(:smtp_envelope_to)
end

it 'should default to destinations' do
message = Mail::Message.new do
to 'to'
cc 'cc'
bcc 'bcc'
end
message.smtp_envelope_to.should eq message.destinations
end

it 'can be overridden' do
message = Mail::Message.new { to 'to' }

message.smtp_envelope_to = 'envelope_to'
message.smtp_envelope_to.should eq %w(envelope_to)

message.smtp_envelope_to = 'declared_to'
message.smtp_envelope_to.should eq %w(declared_to)

message.smtp_envelope_to = nil
message.smtp_envelope_to.should eq %w(to)
end
end

end
24 changes: 12 additions & 12 deletions spec/mail/network/delivery_methods/exim_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
Mail::Exim.should_receive(:call).with('/usr/sbin/exim',
'-i -t -f "[email protected]" --',
'"[email protected]" "[email protected]"',
mail)
mail.encoded)
mail.deliver!
end

Expand All @@ -54,7 +54,7 @@
Mail::Exim.should_receive(:call).with('/usr/sbin/exim',
'-i -t -f "[email protected]" --',
'"[email protected]"',
mail)
mail.encoded)

mail.deliver

Expand All @@ -77,7 +77,7 @@
Mail::Exim.should_receive(:call).with('/usr/sbin/exim',
'-i -t -f "[email protected]" --',
'"[email protected]"',
mail)
mail.encoded)

mail.deliver
end
Expand All @@ -98,7 +98,7 @@
Mail::Exim.should_receive(:call).with('/usr/sbin/exim',
'-i -t -f "[email protected]" --',
'"[email protected]"',
mail)
mail.encoded)
mail.deliver
end

Expand All @@ -118,7 +118,7 @@
Mail::Exim.should_receive(:call).with('/usr/sbin/exim',
'-i -t -f "\"from+suffix test\"@test.lindsaar.net" --',
'"[email protected]"',
mail)
mail.encoded)
mail.deliver
end

Expand All @@ -135,7 +135,7 @@
Mail::Exim.should_receive(:call).with('/usr/sbin/exim',
'-i -t -f "[email protected]" --',
'"[email protected]"',
mail)
mail.encoded)
mail.deliver
end
end
Expand All @@ -152,9 +152,9 @@
end

Mail::Exim.should_receive(:call).with('/usr/sbin/exim',
'-f "[email protected]" --',
' -f "[email protected]" --',
'"[email protected]" "[email protected]"',
mail)
mail.encoded)
mail.deliver!
end

Expand All @@ -170,9 +170,9 @@
end

Mail::Exim.should_receive(:call).with('/usr/sbin/exim',
"-f \"\\\"foo\\\\\\\"\\;touch /tmp/PWNED\\;\\\\\\\"\\\"@blah.com\" --",
" -f \"\\\"foo\\\\\\\"\\;touch /tmp/PWNED\\;\\\\\\\"\\\"@blah.com\" --",
'"[email protected]"',
mail)
mail.encoded)
mail.deliver!
end

Expand All @@ -186,7 +186,7 @@
subject "Email with no sender"
body "body"
end
end.should raise_error('A sender (Return-Path, Sender or From) required to send a message')
end.should raise_error('An SMTP From address is required to send a message. Set the message smtp_envelope_from, return_path, sender, or from address.')
end

it "should raise an error if no recipient if defined" do
Expand All @@ -199,6 +199,6 @@
subject "Email with no recipient"
body "body"
end
end.should raise_error('At least one recipient (To, Cc or Bcc) is required to send a message')
end.should raise_error('An SMTP To address is required to send a message. Set the message smtp_envelope_to, to, cc, or bcc address.')
end
end
4 changes: 2 additions & 2 deletions spec/mail/network/delivery_methods/file_delivery_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@
subject "Email with no sender"
body "body"
end
end.should raise_error('A sender (Return-Path, Sender or From) required to send a message')
end.should raise_error('An SMTP From address is required to send a message. Set the message smtp_envelope_from, return_path, sender, or from address.')
end

it "should raise an error if no recipient if defined" do
Expand All @@ -114,7 +114,7 @@
subject "Email with no recipient"
body "body"
end
end.should raise_error('At least one recipient (To, Cc or Bcc) is required to send a message')
end.should raise_error('An SMTP To address is required to send a message. Set the message smtp_envelope_to, to, cc, or bcc address.')
end

end
Expand Down
Loading