mail 2.7.1 → 2.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +135 -59
  3. data/lib/mail/attachments_list.rb +5 -6
  4. data/lib/mail/body.rb +51 -74
  5. data/lib/mail/constants.rb +27 -5
  6. data/lib/mail/elements/address.rb +27 -27
  7. data/lib/mail/elements/address_list.rb +1 -1
  8. data/lib/mail/elements/content_disposition_element.rb +1 -1
  9. data/lib/mail/elements/content_location_element.rb +1 -1
  10. data/lib/mail/elements/content_transfer_encoding_element.rb +1 -1
  11. data/lib/mail/elements/content_type_element.rb +8 -4
  12. data/lib/mail/elements/date_time_element.rb +1 -1
  13. data/lib/mail/elements/envelope_from_element.rb +13 -7
  14. data/lib/mail/elements/message_ids_element.rb +14 -5
  15. data/lib/mail/elements/mime_version_element.rb +1 -1
  16. data/lib/mail/elements/phrase_list.rb +7 -2
  17. data/lib/mail/elements/received_element.rb +20 -6
  18. data/lib/mail/encodings/7bit.rb +5 -0
  19. data/lib/mail/encodings/base64.rb +2 -2
  20. data/lib/mail/encodings/quoted_printable.rb +2 -2
  21. data/lib/mail/encodings/unix_to_unix.rb +1 -1
  22. data/lib/mail/encodings.rb +32 -61
  23. data/lib/mail/envelope.rb +11 -14
  24. data/lib/mail/field.rb +100 -86
  25. data/lib/mail/field_list.rb +61 -8
  26. data/lib/mail/fields/bcc_field.rb +33 -52
  27. data/lib/mail/fields/cc_field.rb +27 -49
  28. data/lib/mail/fields/comments_field.rb +26 -37
  29. data/lib/mail/fields/common_address_field.rb +162 -0
  30. data/lib/mail/fields/common_date_field.rb +56 -0
  31. data/lib/mail/fields/common_field.rb +77 -0
  32. data/lib/mail/fields/common_message_id_field.rb +41 -0
  33. data/lib/mail/fields/content_description_field.rb +6 -14
  34. data/lib/mail/fields/content_disposition_field.rb +11 -38
  35. data/lib/mail/fields/content_id_field.rb +23 -51
  36. data/lib/mail/fields/content_location_field.rb +10 -25
  37. data/lib/mail/fields/content_transfer_encoding_field.rb +30 -31
  38. data/lib/mail/fields/content_type_field.rb +49 -75
  39. data/lib/mail/fields/date_field.rb +22 -51
  40. data/lib/mail/fields/from_field.rb +27 -49
  41. data/lib/mail/fields/in_reply_to_field.rb +37 -49
  42. data/lib/mail/fields/keywords_field.rb +17 -31
  43. data/lib/mail/fields/message_id_field.rb +24 -71
  44. data/lib/mail/fields/mime_version_field.rb +18 -30
  45. data/lib/mail/fields/named_structured_field.rb +10 -0
  46. data/lib/mail/fields/named_unstructured_field.rb +10 -0
  47. data/lib/mail/fields/optional_field.rb +5 -7
  48. data/lib/mail/fields/{common/parameter_hash.rb → parameter_hash.rb} +12 -10
  49. data/lib/mail/fields/received_field.rb +42 -57
  50. data/lib/mail/fields/references_field.rb +34 -49
  51. data/lib/mail/fields/reply_to_field.rb +27 -49
  52. data/lib/mail/fields/resent_bcc_field.rb +27 -49
  53. data/lib/mail/fields/resent_cc_field.rb +27 -49
  54. data/lib/mail/fields/resent_date_field.rb +4 -29
  55. data/lib/mail/fields/resent_from_field.rb +27 -49
  56. data/lib/mail/fields/resent_message_id_field.rb +4 -29
  57. data/lib/mail/fields/resent_sender_field.rb +26 -56
  58. data/lib/mail/fields/resent_to_field.rb +27 -49
  59. data/lib/mail/fields/return_path_field.rb +49 -54
  60. data/lib/mail/fields/sender_field.rb +33 -55
  61. data/lib/mail/fields/structured_field.rb +2 -30
  62. data/lib/mail/fields/subject_field.rb +8 -11
  63. data/lib/mail/fields/to_field.rb +27 -49
  64. data/lib/mail/fields/unstructured_field.rb +15 -48
  65. data/lib/mail/fields.rb +9 -0
  66. data/lib/mail/header.rb +69 -110
  67. data/lib/mail/mail.rb +32 -27
  68. data/lib/mail/matchers/attachment_matchers.rb +15 -0
  69. data/lib/mail/message.rb +67 -78
  70. data/lib/mail/multibyte/chars.rb +9 -167
  71. data/lib/mail/multibyte/unicode.rb +1 -1
  72. data/lib/mail/multibyte/utils.rb +26 -43
  73. data/lib/mail/multibyte.rb +1 -11
  74. data/lib/mail/network/delivery_methods/exim.rb +5 -4
  75. data/lib/mail/network/delivery_methods/file_delivery.rb +13 -12
  76. data/lib/mail/network/delivery_methods/logger_delivery.rb +2 -5
  77. data/lib/mail/network/delivery_methods/sendmail.rb +27 -35
  78. data/lib/mail/network/delivery_methods/smtp.rb +81 -30
  79. data/lib/mail/network/delivery_methods/smtp_connection.rb +10 -19
  80. data/lib/mail/network/delivery_methods/test_mailer.rb +8 -6
  81. data/lib/mail/network/retriever_methods/base.rb +13 -13
  82. data/lib/mail/network/retriever_methods/imap.rb +8 -5
  83. data/lib/mail/network/retriever_methods/pop3.rb +22 -22
  84. data/lib/mail/network/retriever_methods/test_retriever.rb +2 -1
  85. data/lib/mail/parsers/address_lists_parser.rb +33073 -33064
  86. data/lib/mail/parsers/address_lists_parser.rl +11 -0
  87. data/lib/mail/parsers/content_disposition_parser.rb +837 -828
  88. data/lib/mail/parsers/content_disposition_parser.rl +11 -0
  89. data/lib/mail/parsers/content_location_parser.rb +773 -764
  90. data/lib/mail/parsers/content_location_parser.rl +12 -1
  91. data/lib/mail/parsers/content_transfer_encoding_parser.rb +477 -468
  92. data/lib/mail/parsers/content_transfer_encoding_parser.rl +11 -0
  93. data/lib/mail/parsers/content_type_parser.rb +974 -965
  94. data/lib/mail/parsers/content_type_parser.rl +11 -0
  95. data/lib/mail/parsers/date_time_parser.rb +841 -832
  96. data/lib/mail/parsers/date_time_parser.rl +11 -0
  97. data/lib/mail/parsers/envelope_from_parser.rb +3627 -3530
  98. data/lib/mail/parsers/envelope_from_parser.rl +11 -0
  99. data/lib/mail/parsers/message_ids_parser.rb +5110 -2800
  100. data/lib/mail/parsers/message_ids_parser.rl +16 -1
  101. data/lib/mail/parsers/mime_version_parser.rb +466 -457
  102. data/lib/mail/parsers/mime_version_parser.rl +11 -0
  103. data/lib/mail/parsers/phrase_lists_parser.rb +839 -830
  104. data/lib/mail/parsers/phrase_lists_parser.rl +12 -1
  105. data/lib/mail/parsers/received_parser.rb +8692 -8683
  106. data/lib/mail/parsers/received_parser.rl +11 -0
  107. data/lib/mail/parsers/rfc5322.rl +28 -13
  108. data/lib/mail/parsers.rb +11 -17
  109. data/lib/mail/part.rb +24 -28
  110. data/lib/mail/parts_list.rb +57 -0
  111. data/lib/mail/smtp_envelope.rb +57 -0
  112. data/lib/mail/utilities.rb +321 -69
  113. data/lib/mail/version.rb +1 -1
  114. data/lib/mail/yaml.rb +30 -0
  115. data/lib/mail.rb +2 -22
  116. metadata +83 -19
  117. data/lib/mail/check_delivery_params.rb +0 -60
  118. data/lib/mail/core_extensions/smtp.rb +0 -28
  119. data/lib/mail/core_extensions/string.rb +0 -17
  120. data/lib/mail/fields/common/address_container.rb +0 -17
  121. data/lib/mail/fields/common/common_address.rb +0 -161
  122. data/lib/mail/fields/common/common_date.rb +0 -36
  123. data/lib/mail/fields/common/common_field.rb +0 -52
  124. data/lib/mail/fields/common/common_message_id.rb +0 -49
  125. data/lib/mail/version_specific/ruby_1_8.rb +0 -163
  126. data/lib/mail/version_specific/ruby_1_9.rb +0 -278
@@ -3,59 +3,42 @@
3
3
 
4
4
  module Mail #:nodoc:
5
5
  module Multibyte #:nodoc:
6
- if RUBY_VERSION >= "1.9"
7
- # Returns a regular expression that matches valid characters in the current encoding
8
- def self.valid_character
9
- VALID_CHARACTER[Encoding.default_external.to_s]
10
- end
11
- else
12
- def self.valid_character
13
- case $KCODE
14
- when 'UTF8'
15
- VALID_CHARACTER['UTF-8']
16
- when 'SJIS'
17
- VALID_CHARACTER['Shift_JIS']
18
- end
19
- end
6
+ # Returns a regular expression that matches valid characters in the current encoding
7
+ def self.valid_character
8
+ VALID_CHARACTER[Encoding.default_external.to_s]
20
9
  end
21
10
 
22
- if 'string'.respond_to?(:valid_encoding?)
23
- # Verifies the encoding of a string
24
- def self.verify(string)
25
- string.valid_encoding?
26
- end
27
- else
28
- def self.verify(string)
29
- if expression = valid_character
30
- # Splits the string on character boundaries, which are determined based on $KCODE.
31
- string.split(//).all? { |c| expression =~ c }
32
- else
33
- true
34
- end
11
+ # Returns true if string has valid utf-8 encoding
12
+ def self.is_utf8?(string)
13
+ case string.encoding
14
+ when Encoding::UTF_8
15
+ verify(string)
16
+ when Encoding::ASCII_8BIT, Encoding::US_ASCII
17
+ verify(to_utf8(string))
18
+ else
19
+ false
35
20
  end
36
21
  end
37
22
 
23
+ # Verifies the encoding of a string
24
+ def self.verify(string)
25
+ string.valid_encoding?
26
+ end
27
+
38
28
  # Verifies the encoding of the string and raises an exception when it's not valid
39
29
  def self.verify!(string)
40
30
  raise EncodingError.new("Found characters with invalid encoding") unless verify(string)
41
31
  end
42
32
 
43
- if 'string'.respond_to?(:force_encoding)
44
- # Removes all invalid characters from the string.
45
- #
46
- # Note: this method is a no-op in Ruby 1.9
47
- def self.clean(string)
48
- string
49
- end
50
- else
51
- def self.clean(string)
52
- if expression = valid_character
53
- # Splits the string on character boundaries, which are determined based on $KCODE.
54
- string.split(//).grep(expression).join
55
- else
56
- string
57
- end
58
- end
33
+ # Removes all invalid characters from the string.
34
+ #
35
+ # Note: this method is a no-op in Ruby 1.9
36
+ def self.clean(string)
37
+ string
38
+ end
39
+
40
+ def self.to_utf8(string)
41
+ string.dup.force_encoding(Encoding::UTF_8)
59
42
  end
60
43
  end
61
44
  end
@@ -19,7 +19,6 @@ module Mail #:nodoc:
19
19
 
20
20
  self.proxy_class = Mail::Multibyte::Chars
21
21
 
22
- if RUBY_VERSION >= "1.9"
23
22
  # == Multibyte proxy
24
23
  #
25
24
  # +mb_chars+ is a multibyte safe proxy for string methods.
@@ -54,21 +53,12 @@ module Mail #:nodoc:
54
53
  # For more information about the methods defined on the Chars proxy see Mail::Multibyte::Chars. For
55
54
  # information about how to change the default Multibyte behaviour see Mail::Multibyte.
56
55
  def self.mb_chars(str)
57
- if proxy_class.consumes?(str)
56
+ if is_utf8?(str)
58
57
  proxy_class.new(str)
59
58
  else
60
59
  str
61
60
  end
62
61
  end
63
- else
64
- def self.mb_chars(str)
65
- if proxy_class.wants?(str)
66
- proxy_class.new(str)
67
- else
68
- str
69
- end
70
- end
71
- end
72
62
 
73
63
  # Regular expressions that describe valid byte sequences for a character
74
64
  VALID_CHARACTER = {
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
- module Mail
3
2
 
3
+ module Mail
4
4
  # A delivery method implementation which sends via exim.
5
5
  #
6
6
  # To use this, first find out where the exim binary is on your computer,
@@ -39,11 +39,12 @@ module Mail
39
39
  class Exim < Sendmail
40
40
  DEFAULTS = {
41
41
  :location => '/usr/sbin/exim',
42
- :arguments => '-i -t'
42
+ :arguments => %w[ -i -t ]
43
43
  }
44
44
 
45
- def self.call(path, arguments, destinations, encoded_message)
46
- super path, arguments, nil, encoded_message
45
+ # Uses -t option to extract recipients from the message.
46
+ def destinations_for(envelope)
47
+ nil
47
48
  end
48
49
  end
49
50
  end
@@ -1,32 +1,28 @@
1
1
  # frozen_string_literal: true
2
- require 'mail/check_delivery_params'
2
+ require 'mail/smtp_envelope'
3
3
 
4
4
  module Mail
5
5
  # FileDelivery class delivers emails into multiple files based on the destination
6
6
  # address. Each file is appended to if it already exists.
7
- #
7
+ #
8
8
  # So if you have an email going to fred@test, bob@test, joe@anothertest, and you
9
9
  # set your location path to /path/to/mails then FileDelivery will create the directory
10
10
  # if it does not exist, and put one copy of the email in three files, called
11
11
  # by their message id
12
- #
12
+ #
13
13
  # Make sure the path you specify with :location is writable by the Ruby process
14
14
  # running Mail.
15
15
  class FileDelivery
16
- if RUBY_VERSION >= '1.9.1'
17
- require 'fileutils'
18
- else
19
- require 'ftools'
20
- end
16
+ require 'fileutils'
21
17
 
22
18
  attr_accessor :settings
23
19
 
24
20
  def initialize(values)
25
- self.settings = { :location => './mails' }.merge!(values)
21
+ self.settings = { :location => './mails', :extension => '' }.merge!(values)
26
22
  end
27
23
 
28
24
  def deliver!(mail)
29
- Mail::CheckDeliveryParams.check(mail)
25
+ envelope = Mail::SmtpEnvelope.new(mail)
30
26
 
31
27
  if ::File.respond_to?(:makedirs)
32
28
  ::File.makedirs settings[:location]
@@ -34,8 +30,13 @@ module Mail
34
30
  ::FileUtils.mkdir_p settings[:location]
35
31
  end
36
32
 
37
- mail.destinations.uniq.each do |to|
38
- ::File.open(::File.join(settings[:location], File.basename(to.to_s)), 'a') { |f| "#{f.write(mail.encoded)}\r\n\r\n" }
33
+ envelope.to.uniq.each do |to|
34
+ path = ::File.join(settings[:location], File.basename(to.to_s+settings[:extension]))
35
+
36
+ ::File.open(path, 'a') do |f|
37
+ f.write envelope.message
38
+ f.write "\r\n\r\n"
39
+ end
39
40
  end
40
41
  end
41
42
  end
@@ -1,9 +1,7 @@
1
- require 'mail/check_delivery_params'
1
+ require 'mail/smtp_envelope'
2
2
 
3
3
  module Mail
4
4
  class LoggerDelivery
5
- include Mail::CheckDeliveryParams
6
-
7
5
  attr_reader :logger, :severity, :settings
8
6
 
9
7
  def initialize(settings)
@@ -13,8 +11,7 @@ module Mail
13
11
  end
14
12
 
15
13
  def deliver!(mail)
16
- Mail::CheckDeliveryParams.check(mail)
17
- logger.log(severity) { mail.encoded }
14
+ logger.log(severity) { Mail::SmtpEnvelope.new(mail).message }
18
15
  end
19
16
 
20
17
  private
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- require 'mail/check_delivery_params'
2
+ require 'mail/smtp_envelope'
3
3
 
4
4
  module Mail
5
5
  # A delivery method implementation which sends via sendmail.
@@ -40,56 +40,48 @@ module Mail
40
40
  class Sendmail
41
41
  DEFAULTS = {
42
42
  :location => '/usr/sbin/sendmail',
43
- :arguments => '-i'
43
+ :arguments => %w[ -i ]
44
44
  }
45
45
 
46
46
  attr_accessor :settings
47
47
 
48
+ class DeliveryError < StandardError
49
+ end
50
+
48
51
  def initialize(values)
49
52
  self.settings = self.class::DEFAULTS.merge(values)
53
+ raise ArgumentError, ":arguments expected to be an Array of individual string args" if settings[:arguments].is_a?(String)
54
+ end
55
+
56
+ def destinations_for(envelope)
57
+ envelope.to
50
58
  end
51
59
 
52
60
  def deliver!(mail)
53
- smtp_from, smtp_to, message = Mail::CheckDeliveryParams.check(mail)
61
+ envelope = Mail::SmtpEnvelope.new(mail)
54
62
 
55
- from = "-f #{self.class.shellquote(smtp_from)}" if smtp_from
56
- to = smtp_to.map { |_to| self.class.shellquote(_to) }.join(' ')
63
+ command = [settings[:location]]
64
+ command.concat Array(settings[:arguments])
65
+ command.concat [ '-f', envelope.from ] if envelope.from
57
66
 
58
- arguments = "#{settings[:arguments]} #{from} --"
59
- self.class.call(settings[:location], arguments, to, message)
60
- end
67
+ if destinations = destinations_for(envelope)
68
+ command.push '--'
69
+ command.concat destinations
70
+ end
61
71
 
62
- def self.call(path, arguments, destinations, encoded_message)
63
- popen "#{path} #{arguments} #{destinations}" do |io|
64
- io.puts ::Mail::Utilities.binary_unsafe_to_lf(encoded_message)
72
+ popen(command) do |io|
73
+ io.puts ::Mail::Utilities.binary_unsafe_to_lf(envelope.message)
65
74
  io.flush
66
75
  end
67
76
  end
68
77
 
69
- if RUBY_VERSION < '1.9.0'
70
- def self.popen(command, &block)
71
- IO.popen "#{command} 2>&1", 'w+', &block
72
- end
73
- else
74
- def self.popen(command, &block)
75
- IO.popen command, 'w+', :err => :out, &block
78
+ private
79
+ def popen(command, &block)
80
+ IO.popen(command, 'w+', :err => :out, &block).tap do
81
+ if $?.exitstatus != 0
82
+ raise DeliveryError, "Delivery failed with exitstatus #{$?.exitstatus}: #{command.inspect}"
83
+ end
84
+ end
76
85
  end
77
- end
78
-
79
- # The following is an adaptation of ruby 1.9.2's shellwords.rb file,
80
- # with the following modifications:
81
- #
82
- # - Wraps in double quotes
83
- # - Allows '+' to accept email addresses with them
84
- # - Allows '~' as it is not unescaped in double quotes
85
- def self.shellquote(address)
86
- # Process as a single byte sequence because not all shell
87
- # implementations are multibyte aware.
88
- #
89
- # A LF cannot be escaped with a backslash because a backslash + LF
90
- # combo is regarded as line continuation and simply ignored. Strip it.
91
- escaped = address.gsub(/([^A-Za-z0-9_\s\+\-.,:\/@~])/n, "\\\\\\1").gsub("\n", '')
92
- %("#{escaped}")
93
- end
94
86
  end
95
87
  end
@@ -1,20 +1,20 @@
1
1
  # frozen_string_literal: true
2
- require 'mail/check_delivery_params'
2
+ require 'mail/smtp_envelope'
3
3
 
4
4
  module Mail
5
5
  # == Sending Email with SMTP
6
- #
6
+ #
7
7
  # Mail allows you to send emails using SMTP. This is done by wrapping Net::SMTP in
8
8
  # an easy to use manner.
9
- #
9
+ #
10
10
  # === Sending via SMTP server on Localhost
11
- #
11
+ #
12
12
  # Sending locally (to a postfix or sendmail server running on localhost) requires
13
13
  # no special setup. Just to Mail.deliver &block or message.deliver! and it will
14
14
  # be sent in this method.
15
- #
15
+ #
16
16
  # === Sending via MobileMe
17
- #
17
+ #
18
18
  # Mail.defaults do
19
19
  # delivery_method :smtp, { :address => "smtp.me.com",
20
20
  # :port => 587,
@@ -22,11 +22,11 @@ module Mail
22
22
  # :user_name => '<username>',
23
23
  # :password => '<password>',
24
24
  # :authentication => 'plain',
25
- # :enable_starttls_auto => true }
25
+ # :enable_starttls => :auto }
26
26
  # end
27
- #
27
+ #
28
28
  # === Sending via GMail
29
- #
29
+ #
30
30
  # Mail.defaults do
31
31
  # delivery_method :smtp, { :address => "smtp.gmail.com",
32
32
  # :port => 587,
@@ -34,9 +34,17 @@ module Mail
34
34
  # :user_name => '<username>',
35
35
  # :password => '<password>',
36
36
  # :authentication => 'plain',
37
- # :enable_starttls_auto => true }
37
+ # :enable_starttls => :auto }
38
38
  # end
39
39
  #
40
+ # === Configuring TLS/SSL and STARTTLS
41
+ #
42
+ # A few remarks:
43
+ # - when enabling `tls` (or `ssl`), setting (truthy values for) either `enable_starttls` or `enable_starttls_auto` will raise an ArgumentError as TLS and STARTTLS are mutually exclusive.
44
+ # - to configure STARTTLS, use the `enable_starttls`-flag (instead of a combination of `enable_starttls` and `enable_starttls_auto`). Acceptable values are `:always`, `:auto` and `false`.
45
+ # - when providing a truthy value for `enable_starttls`, the `enable_starttls_auto`-flag will be ignored.
46
+ # - when none of `tls`, `ssl`, `enable_starttls` or `enable_starttls_auto` is set, the fallback will be `enable_starttls` `:auto`.
47
+ #
40
48
  # === Certificate verification
41
49
  #
42
50
  # When using TLS, some mail servers provide certificates that are self-signed
@@ -48,30 +56,30 @@ module Mail
48
56
  # verify mode constant (OpenSSL::SSL::VERIFY_NONE, OpenSSL::SSL::VERIFY_PEER),
49
57
  # or a string containing the name of an OpenSSL verify mode (none, peer).
50
58
  #
51
- # === Others
52
- #
59
+ # === Others
60
+ #
53
61
  # Feel free to send me other examples that were tricky
54
- #
62
+ #
55
63
  # === Delivering the email
56
- #
64
+ #
57
65
  # Once you have the settings right, sending the email is done by:
58
- #
66
+ #
59
67
  # Mail.deliver do
60
68
  # to 'mikel@test.lindsaar.net'
61
69
  # from 'ada@test.lindsaar.net'
62
70
  # subject 'testing sendmail'
63
71
  # body 'testing sendmail'
64
72
  # end
65
- #
73
+ #
66
74
  # Or by calling deliver on a Mail message
67
- #
75
+ #
68
76
  # mail = Mail.new do
69
77
  # to 'mikel@test.lindsaar.net'
70
78
  # from 'ada@test.lindsaar.net'
71
79
  # subject 'testing sendmail'
72
80
  # body 'testing sendmail'
73
81
  # end
74
- #
82
+ #
75
83
  # mail.deliver!
76
84
  class SMTP
77
85
  attr_accessor :settings
@@ -84,12 +92,12 @@ module Mail
84
92
  :password => nil,
85
93
  :authentication => nil,
86
94
  :enable_starttls => nil,
87
- :enable_starttls_auto => true,
95
+ :enable_starttls_auto => nil,
88
96
  :openssl_verify_mode => nil,
89
97
  :ssl => nil,
90
98
  :tls => nil,
91
- :open_timeout => nil,
92
- :read_timeout => nil
99
+ :open_timeout => 5,
100
+ :read_timeout => 5
93
101
  }
94
102
 
95
103
  def initialize(values)
@@ -105,23 +113,66 @@ module Mail
105
113
  end
106
114
 
107
115
  private
116
+ # `k` is said to be provided when `settings` has a non-nil value for `k`.
117
+ def setting_provided?(k)
118
+ !settings[k].nil?
119
+ end
120
+
121
+ # Yields one of `:always`, `:auto` or `false` based on `enable_starttls` and `enable_starttls_auto` flags.
122
+ # Yields `false` when `smtp_tls?`.
123
+ # Else defaults to `:auto` when neither `enable_starttls*` flag is provided.
124
+ # Providing a truthy value for `enable_starttls` will ignore `enable_starttls_auto`.
125
+ def smtp_starttls
126
+ return false if smtp_tls?
127
+
128
+ if setting_provided?(:enable_starttls) && settings[:enable_starttls]
129
+ # enable_starttls: provided and truthy
130
+ case settings[:enable_starttls]
131
+ when :auto then :auto
132
+ when :always then :always
133
+ else
134
+ :always
135
+ end
136
+ else
137
+ # enable_starttls: not provided or false
138
+ if setting_provided?(:enable_starttls_auto)
139
+ settings[:enable_starttls_auto] ? :auto : false
140
+ else
141
+ # enable_starttls_auto: not provided
142
+ # enable_starttls: when provided then false
143
+ # use :auto when neither enable_starttls* provided
144
+ setting_provided?(:enable_starttls) ? false : :auto
145
+ end
146
+ end
147
+ end
148
+
149
+ def smtp_tls?
150
+ (setting_provided?(:tls) && settings[:tls]) || (setting_provided?(:ssl) && settings[:ssl])
151
+ end
152
+
108
153
  def start_smtp_session(&block)
109
154
  build_smtp_session.start(settings[:domain], settings[:user_name], settings[:password], settings[:authentication], &block)
110
155
  end
111
156
 
112
157
  def build_smtp_session
158
+ if smtp_tls? && (settings[:enable_starttls] || settings[:enable_starttls_auto])
159
+ raise ArgumentError, ":enable_starttls and :tls are mutually exclusive. Set :tls if you're on an SMTPS connection. Set :enable_starttls if you're on an SMTP connection and using STARTTLS for secure TLS upgrade."
160
+ end
161
+
113
162
  Net::SMTP.new(settings[:address], settings[:port]).tap do |smtp|
114
- if settings[:tls] || settings[:ssl]
115
- if smtp.respond_to?(:enable_tls)
116
- smtp.enable_tls(ssl_context)
117
- end
118
- elsif settings[:enable_starttls]
119
- if smtp.respond_to?(:enable_starttls)
163
+ if smtp_tls?
164
+ smtp.disable_starttls
165
+ smtp.enable_tls(ssl_context)
166
+ else
167
+ smtp.disable_tls
168
+
169
+ case smtp_starttls
170
+ when :always
120
171
  smtp.enable_starttls(ssl_context)
121
- end
122
- elsif settings[:enable_starttls_auto]
123
- if smtp.respond_to?(:enable_starttls_auto)
172
+ when :auto
124
173
  smtp.enable_starttls_auto(ssl_context)
174
+ else
175
+ smtp.disable_starttls
125
176
  end
126
177
  end
127
178
 
@@ -1,41 +1,41 @@
1
1
  # frozen_string_literal: true
2
- require 'mail/check_delivery_params'
2
+ require 'mail/smtp_envelope'
3
3
 
4
4
  module Mail
5
5
  # == Sending Email with SMTP
6
- #
6
+ #
7
7
  # Mail allows you to send emails using an open SMTP connection. This is done by
8
8
  # passing a created Net::SMTP object. This way we can get better performance to
9
9
  # our local mail server by reducing the number of connections at any one time.
10
10
  #
11
11
  # === Sending via SMTP server on Localhost
12
- #
12
+ #
13
13
  # To send mail open a connection with Net::Smtp using any options you like
14
14
  # === Delivering the email
15
- #
15
+ #
16
16
  # Once you have the settings right, sending the email is done by:
17
17
  #
18
18
  # smtp_conn = Net::SMTP.start(settings[:address], settings[:port])
19
19
  # Mail.defaults do
20
20
  # delivery_method :smtp_connection, { :connection => smtp_conn }
21
21
  # end
22
- #
22
+ #
23
23
  # Mail.deliver do
24
24
  # to 'mikel@test.lindsaar.net'
25
25
  # from 'ada@test.lindsaar.net'
26
26
  # subject 'testing sendmail'
27
27
  # body 'testing sendmail'
28
28
  # end
29
- #
29
+ #
30
30
  # Or by calling deliver on a Mail message
31
- #
31
+ #
32
32
  # mail = Mail.new do
33
33
  # to 'mikel@test.lindsaar.net'
34
34
  # from 'ada@test.lindsaar.net'
35
35
  # subject 'testing sendmail'
36
36
  # body 'testing sendmail'
37
37
  # end
38
- #
38
+ #
39
39
  # mail.deliver!
40
40
  class SMTPConnection
41
41
  attr_accessor :smtp, :settings
@@ -49,18 +49,9 @@ module Mail
49
49
  # Send the message via SMTP.
50
50
  # The from and to attributes are optional. If not set, they are retrieve from the Message.
51
51
  def deliver!(mail)
52
- smtp_from, smtp_to, message = Mail::CheckDeliveryParams.check(mail)
53
-
54
- response = smtp.sendmail(dot_stuff(message), smtp_from, smtp_to)
55
-
52
+ envelope = Mail::SmtpEnvelope.new(mail)
53
+ response = smtp.sendmail(envelope.message, envelope.from, envelope.to)
56
54
  settings[:return_response] ? response : self
57
55
  end
58
-
59
- private
60
- # This is Net::SMTP's job, but before Ruby 2.x it does not dot-stuff
61
- # an unterminated last line: https://bugs.ruby-lang.org/issues/9627
62
- def dot_stuff(message)
63
- message.gsub(/(\r\n\.)(\r\n|$)/, '\1.\2')
64
- end
65
56
  end
66
57
  end
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
- require 'mail/check_delivery_params'
2
+ require 'mail/smtp_envelope'
3
3
 
4
4
  module Mail
5
5
  # The TestMailer is a bare bones mailer that does nothing. It is useful
6
6
  # when you are testing.
7
- #
7
+ #
8
8
  # It also provides a template of the minimum methods you require to implement
9
9
  # if you want to make a custom mailer for Mail
10
10
  class TestMailer
@@ -14,11 +14,11 @@ module Mail
14
14
  end
15
15
 
16
16
  # Allows you to over write the default deliveries store from an array to some
17
- # other object. If you just want to clear the store,
17
+ # other object. If you just want to clear the store,
18
18
  # call TestMailer.deliveries.clear.
19
- #
19
+ #
20
20
  # If you place another object here, please make sure it responds to:
21
- #
21
+ #
22
22
  # * << (message)
23
23
  # * clear
24
24
  # * length
@@ -35,7 +35,9 @@ module Mail
35
35
  end
36
36
 
37
37
  def deliver!(mail)
38
- Mail::CheckDeliveryParams.check(mail)
38
+ # Create the envelope to validate it
39
+ Mail::SmtpEnvelope.new(mail)
40
+
39
41
  Mail::TestMailer.deliveries << mail
40
42
  end
41
43
  end
@@ -11,38 +11,38 @@ module Mail
11
11
  # count: number of emails to retrieve. The default value is 1.
12
12
  # order: order of emails returned. Possible values are :asc or :desc. Default value is :asc.
13
13
  #
14
- def first(options = {}, &block)
15
- options ||= {}
14
+ def first(options = nil, &block)
15
+ options = options ? Hash[options] : {}
16
16
  options[:what] = :first
17
17
  options[:count] ||= 1
18
18
  find(options, &block)
19
19
  end
20
-
20
+
21
21
  # Get the most recent received email(s)
22
22
  #
23
23
  # Possible options:
24
24
  # count: number of emails to retrieve. The default value is 1.
25
25
  # order: order of emails returned. Possible values are :asc or :desc. Default value is :asc.
26
26
  #
27
- def last(options = {}, &block)
28
- options ||= {}
27
+ def last(options = nil, &block)
28
+ options = options ? Hash[options] : {}
29
29
  options[:what] = :last
30
30
  options[:count] ||= 1
31
31
  find(options, &block)
32
32
  end
33
-
33
+
34
34
  # Get all emails.
35
35
  #
36
36
  # Possible options:
37
37
  # order: order of emails returned. Possible values are :asc or :desc. Default value is :asc.
38
38
  #
39
- def all(options = {}, &block)
40
- options ||= {}
39
+ def all(options = nil, &block)
40
+ options = options ? Hash[options] : {}
41
41
  options[:count] = :all
42
42
  find(options, &block)
43
43
  end
44
44
 
45
- # Find emails in the mailbox, and then deletes them. Without any options, the
45
+ # Find emails in the mailbox, and then deletes them. Without any options, the
46
46
  # five last received emails are returned.
47
47
  #
48
48
  # Possible options:
@@ -53,11 +53,11 @@ module Mail
53
53
  # delete_after_find: flag for whether to delete each retreived email after find. Default
54
54
  # is true. Call #find if you would like this to default to false.
55
55
  #
56
- def find_and_delete(options = {}, &block)
57
- options ||= {}
56
+ def find_and_delete(options = nil, &block)
57
+ options = options ? Hash[options] : {}
58
58
  options[:delete_after_find] ||= true
59
- find(options, &block)
60
- end
59
+ find(options, &block)
60
+ end
61
61
 
62
62
  end
63
63