Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: mikel/mail
...
head fork: mikel/mail
  • 4 commits
  • 12 files changed
  • 0 commit comments
  • 1 contributor
View
1  .gitignore
@@ -1,4 +1,5 @@
.bundle
+.rbx
Gemfile.lock
coverage
gems
View
3  Gemfile
@@ -15,6 +15,9 @@ group :test do
gem "rake", "~> 0.8.7"
gem "bundler"
gem "rspec", "~> 1.3.0"
+ gem "rspec-core", "~> 2.4.0"
+ gem "rspec-mocks", "~> 2.4.0"
+ gem "rspec-expectations", "~> 2.4.0"
gem "diff-lcs"
case
when defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
View
2  lib/VERSION
@@ -1,4 +1,4 @@
major:2
minor:3
-patch:0
+patch:1
build:
View
2  lib/mail.rb
@@ -29,7 +29,7 @@ module Mail # :doc:
require 'mail/core_extensions/nil'
require 'mail/core_extensions/object'
require 'mail/core_extensions/string'
- require 'mail/core_extensions/shellwords' unless String.new.respond_to?(:shellescape)
+ require 'mail/core_extensions/shell_escape'
require 'mail/core_extensions/smtp' if RUBY_VERSION < '1.9.3'
require 'mail/indifferent_hash'
View
2  lib/mail/configuration.rb
@@ -33,6 +33,8 @@ def lookup_delivery_method(method)
Mail::SMTP
when :sendmail
Mail::Sendmail
+ when :exim
+ Mail::Exim
when :file
Mail::FileDelivery
when :smtp_connection
View
56 lib/mail/core_extensions/shell_escape.rb
@@ -0,0 +1,56 @@
+# encoding: utf-8
+
+# The following is an adaptation of ruby 1.9.2's shellwords.rb file,
+# it is modified to include '+' in the allowed list to allow for
+# sendmail to accept email addresses as the sender with a + in them
+#
+module Mail
+ module ShellEscape
+ # Escapes a string so that it can be safely used in a Bourne shell
+ # command line.
+ #
+ # Note that a resulted string should be used unquoted and is not
+ # intended for use in double quotes nor in single quotes.
+ #
+ # open("| grep #{Shellwords.escape(pattern)} file") { |pipe|
+ # # ...
+ # }
+ #
+ # +String#shellescape+ is a shorthand for this function.
+ #
+ # open("| grep #{pattern.shellescape} file") { |pipe|
+ # # ...
+ # }
+ #
+ def escape_for_shell(str)
+ # An empty argument will be skipped, so return empty quotes.
+ return "''" if str.empty?
+
+ str = str.dup
+
+ # Process as a single byte sequence because not all shell
+ # implementations are multibyte aware.
+ str.gsub!(/([^A-Za-z0-9_\s\+\-.,:\/@\n])/n, "\\\\\\1")
+
+ # A LF cannot be escaped with a backslash because a backslash + LF
+ # combo is regarded as line continuation and simply ignored.
+ str.gsub!(/\n/, "'\n'")
+
+ return str
+ end
+
+ module_function :escape_for_shell
+ end
+end
+
+class String
+ # call-seq:
+ # str.shellescape => string
+ #
+ # Escapes +str+ so that it can be safely used in a Bourne shell
+ # command line. See +Shellwords::shellescape+ for details.
+ #
+ def escape_for_shell
+ Mail::ShellEscape.escape_for_shell(self)
+ end
+end
View
57 lib/mail/core_extensions/shellwords.rb
@@ -1,57 +0,0 @@
-# encoding: utf-8
-
-# The following is imported from ruby 1.9.2 shellwords.rb
-#
-module Shellwords
- # Escapes a string so that it can be safely used in a Bourne shell
- # command line.
- #
- # Note that a resulted string should be used unquoted and is not
- # intended for use in double quotes nor in single quotes.
- #
- # open("| grep #{Shellwords.escape(pattern)} file") { |pipe|
- # # ...
- # }
- #
- # +String#shellescape+ is a shorthand for this function.
- #
- # open("| grep #{pattern.shellescape} file") { |pipe|
- # # ...
- # }
- #
- def shellescape(str)
- # An empty argument will be skipped, so return empty quotes.
- return "''" if str.empty?
-
- str = str.dup
-
- # Process as a single byte sequence because not all shell
- # implementations are multibyte aware.
- str.gsub!(/([^A-Za-z0-9_\-.,:\/@\n])/n, "\\\\\\1")
-
- # A LF cannot be escaped with a backslash because a backslash + LF
- # combo is regarded as line continuation and simply ignored.
- str.gsub!(/\n/, "'\n'")
-
- return str
- end
-
- module_function :shellescape
-
- class << self
- alias escape shellescape
- end
-
-end
-
-class String
- # call-seq:
- # str.shellescape => string
- #
- # Escapes +str+ so that it can be safely used in a Bourne shell
- # command line. See +Shellwords::shellescape+ for details.
- #
- def shellescape
- Shellwords.escape(self)
- end
-end
View
1  lib/mail/network.rb
@@ -4,6 +4,7 @@ module Mail
autoload :SMTP, 'mail/network/delivery_methods/smtp'
autoload :FileDelivery, 'mail/network/delivery_methods/file_delivery'
autoload :Sendmail, 'mail/network/delivery_methods/sendmail'
+ autoload :Exim, 'mail/network/delivery_methods/exim'
autoload :SMTPConnection, 'mail/network/delivery_methods/smtp_connection'
autoload :TestMailer, 'mail/network/delivery_methods/test_mailer'
View
53 lib/mail/network/delivery_methods/exim.rb
@@ -0,0 +1,53 @@
+module Mail
+
+ # A delivery method implementation which sends via exim.
+ #
+ # To use this, first find out where the exim binary is on your computer,
+ # if you are on a mac or unix box, it is usually in /usr/sbin/exim, this will
+ # be your exim location.
+ #
+ # Mail.defaults do
+ # delivery_method :exim
+ # end
+ #
+ # Or if your exim binary is not at '/usr/sbin/exim'
+ #
+ # Mail.defaults do
+ # delivery_method :exim, :location => '/absolute/path/to/your/exim'
+ # end
+ #
+ # Then just deliver the email as normal:
+ #
+ # Mail.deliver do
+ # to 'mikel@test.lindsaar.net'
+ # from 'ada@test.lindsaar.net'
+ # subject 'testing exim'
+ # body 'testing exim'
+ # end
+ #
+ # Or by calling deliver on a Mail message
+ #
+ # mail = Mail.new do
+ # to 'mikel@test.lindsaar.net'
+ # from 'ada@test.lindsaar.net'
+ # subject 'testing exim'
+ # body 'testing exim'
+ # end
+ #
+ # mail.deliver!
+ class Exim < Sendmail
+
+ def initialize(values)
+ self.settings = { :location => '/usr/sbin/exim',
+ :arguments => '-i -t' }.merge(values)
+ end
+
+ def self.call(path, arguments, mail)
+ IO.popen("#{path} #{arguments}", "w+") do |io|
+ io.puts mail.encoded.to_lf
+ io.flush
+ end
+ end
+
+ end
+end
View
6 lib/mail/network/delivery_methods/sendmail.rb
@@ -45,14 +45,14 @@ def initialize(values)
def deliver!(mail)
envelope_from = mail.return_path || mail.sender || mail.from_addrs.first
- return_path = "-f \"#{envelope_from.to_s.shellescape}\"" if envelope_from
+ return_path = "-f " + '"' + envelope_from.escape_for_shell + '"' if envelope_from
arguments = [settings[:arguments], return_path].compact.join(" ")
- Sendmail.call(settings[:location], arguments, mail.destinations.collect(&:shellescape).join(" "), mail)
+ self.class.call(settings[:location], arguments, mail.destinations.collect(&:shellescape).join(" "), mail)
end
- def Sendmail.call(path, arguments, destinations, mail)
+ def self.call(path, arguments, destinations, mail)
IO.popen("#{path} #{arguments} #{destinations}", "w+") do |io|
io.puts mail.encoded.to_lf
io.flush
View
161 spec/mail/network/delivery_methods/exim_spec.rb
@@ -0,0 +1,161 @@
+# encoding: utf-8
+require 'spec_helper'
+
+describe "exim delivery agent" do
+
+ before(:each) do
+ # Reset all defaults back to original state
+ Mail.defaults do
+ delivery_method :smtp, { :address => "localhost",
+ :port => 25,
+ :domain => 'localhost.localdomain',
+ :user_name => nil,
+ :password => nil,
+ :authentication => nil,
+ :enable_starttls_auto => true }
+ end
+ end
+
+ it "should send an email using exim" do
+ Mail.defaults do
+ delivery_method :exim
+ end
+
+ mail = Mail.new do
+ from 'roger@test.lindsaar.net'
+ to 'marcel@test.lindsaar.net, bob@test.lindsaar.net'
+ subject 'invalid RFC2822'
+ end
+
+ Mail::Exim.should_receive(:call).with('/usr/sbin/exim',
+ '-i -t -f "roger@test.lindsaar.net"',
+ 'marcel@test.lindsaar.net bob@test.lindsaar.net',
+ mail)
+ mail.deliver!
+ end
+
+ describe "return path" do
+
+ it "should send an email with a return-path using exim" do
+ Mail.defaults do
+ delivery_method :exim
+ end
+
+ mail = Mail.new do
+ to "to@test.lindsaar.net"
+ from "from@test.lindsaar.net"
+ sender "sender@test.lindsaar.net"
+ subject "Can't set the return-path"
+ return_path "return@test.lindsaar.net"
+ message_id "<1234@test.lindsaar.net>"
+ body "body"
+ end
+
+ Mail::Exim.should_receive(:call).with('/usr/sbin/exim',
+ '-i -t -f "return@test.lindsaar.net"',
+ 'to@test.lindsaar.net',
+ mail)
+
+ mail.deliver
+
+ end
+
+ it "should use the sender address is no return path is specified" do
+ Mail.defaults do
+ delivery_method :exim
+ end
+
+ mail = Mail.new do
+ to "to@test.lindsaar.net"
+ from "from@test.lindsaar.net"
+ sender "sender@test.lindsaar.net"
+ subject "Can't set the return-path"
+ message_id "<1234@test.lindsaar.net>"
+ body "body"
+ end
+
+ Mail::Exim.should_receive(:call).with('/usr/sbin/exim',
+ '-i -t -f "sender@test.lindsaar.net"',
+ 'to@test.lindsaar.net',
+ mail)
+
+ mail.deliver
+ end
+
+ it "should use the from address is no return path or sender are specified" do
+ Mail.defaults do
+ delivery_method :exim
+ end
+
+ mail = Mail.new do
+ to "to@test.lindsaar.net"
+ from "from@test.lindsaar.net"
+ subject "Can't set the return-path"
+ message_id "<1234@test.lindsaar.net>"
+ body "body"
+ end
+
+ Mail::Exim.should_receive(:call).with('/usr/sbin/exim',
+ '-i -t -f "from@test.lindsaar.net"',
+ 'to@test.lindsaar.net',
+ mail)
+ mail.deliver
+ end
+
+ it "should escape the return path address" do
+ Mail.defaults do
+ delivery_method :exim
+ end
+
+ mail = Mail.new do
+ to 'to@test.lindsaar.net'
+ from '"from+suffix test"@test.lindsaar.net'
+ subject 'Can\'t set the return-path'
+ message_id '<1234@test.lindsaar.net>'
+ body 'body'
+ end
+
+ Mail::Exim.should_receive(:call).with('/usr/sbin/exim',
+ '-i -t -f "\"from+suffix test\"@test.lindsaar.net"',
+ 'to@test.lindsaar.net',
+ mail)
+ mail.deliver
+ end
+ end
+
+ it "should still send an email if the settings have been set to nil" do
+ Mail.defaults do
+ delivery_method :exim, :arguments => nil
+ end
+
+ mail = Mail.new do
+ from 'from@test.lindsaar.net'
+ to 'marcel@test.lindsaar.net, bob@test.lindsaar.net'
+ subject 'invalid RFC2822'
+ end
+
+ Mail::Exim.should_receive(:call).with('/usr/sbin/exim',
+ '-f "from@test.lindsaar.net"',
+ 'marcel@test.lindsaar.net bob@test.lindsaar.net',
+ mail)
+ mail.deliver!
+ end
+
+ it "should escape evil haxxor attemptes" do
+ Mail.defaults do
+ delivery_method :exim, :arguments => nil
+ end
+
+ mail = Mail.new do
+ from '"foo\";touch /tmp/PWNED;\""@blah.com'
+ to 'marcel@test.lindsaar.net'
+ subject 'invalid RFC2822'
+ end
+
+ Mail::Exim.should_receive(:call).with('/usr/sbin/exim',
+ "-f \"\\\"foo\\\\\\\"\\;touch /tmp/PWNED\\;\\\\\\\"\\\"@blah.com\"",
+ 'marcel@test.lindsaar.net',
+ mail)
+ mail.deliver!
+ end
+end
View
21 spec/mail/network/delivery_methods/sendmail_spec.rb
@@ -104,7 +104,6 @@
end
-
it "should still send an email if the settings have been set to nil" do
Mail.defaults do
delivery_method :sendmail, :arguments => nil
@@ -122,4 +121,22 @@
mail)
mail.deliver!
end
-end
+
+ it "should escape evil haxxor attemptes" do
+ Mail.defaults do
+ delivery_method :sendmail, :arguments => nil
+ end
+
+ mail = Mail.new do
+ from '"foo\";touch /tmp/PWNED;\""@blah.com'
+ to 'marcel@test.lindsaar.net'
+ subject 'invalid RFC2822'
+ end
+
+ Mail::Sendmail.should_receive(:call).with('/usr/sbin/sendmail',
+ "-f \"\\\"foo\\\\\\\"\\;touch /tmp/PWNED\\;\\\\\\\"\\\"@blah.com\"",
+ 'marcel@test.lindsaar.net',
+ mail)
+ mail.deliver!
+ end
+end

No commit comments for this range

Something went wrong with that request. Please try again.