mail 2.6.4 → 2.9.0

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 (180) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +208 -156
  3. data/lib/mail/attachments_list.rb +13 -10
  4. data/lib/mail/body.rb +96 -107
  5. data/lib/mail/configuration.rb +2 -0
  6. data/lib/mail/constants.rb +27 -5
  7. data/lib/mail/elements/address.rb +61 -50
  8. data/lib/mail/elements/address_list.rb +11 -19
  9. data/lib/mail/elements/content_disposition_element.rb +9 -16
  10. data/lib/mail/elements/content_location_element.rb +6 -11
  11. data/lib/mail/elements/content_transfer_encoding_element.rb +6 -11
  12. data/lib/mail/elements/content_type_element.rb +16 -23
  13. data/lib/mail/elements/date_time_element.rb +7 -15
  14. data/lib/mail/elements/envelope_from_element.rb +22 -23
  15. data/lib/mail/elements/message_ids_element.rb +18 -13
  16. data/lib/mail/elements/mime_version_element.rb +7 -15
  17. data/lib/mail/elements/phrase_list.rb +12 -10
  18. data/lib/mail/elements/received_element.rb +27 -19
  19. data/lib/mail/encodings/7bit.rb +9 -14
  20. data/lib/mail/encodings/8bit.rb +2 -21
  21. data/lib/mail/encodings/base64.rb +11 -12
  22. data/lib/mail/encodings/binary.rb +3 -22
  23. data/lib/mail/encodings/identity.rb +24 -0
  24. data/lib/mail/encodings/quoted_printable.rb +6 -6
  25. data/lib/mail/encodings/transfer_encoding.rb +38 -29
  26. data/lib/mail/encodings/unix_to_unix.rb +4 -2
  27. data/lib/mail/encodings.rb +83 -56
  28. data/lib/mail/envelope.rb +11 -14
  29. data/lib/mail/field.rb +181 -130
  30. data/lib/mail/field_list.rb +61 -8
  31. data/lib/mail/fields/bcc_field.rb +33 -52
  32. data/lib/mail/fields/cc_field.rb +27 -49
  33. data/lib/mail/fields/comments_field.rb +26 -37
  34. data/lib/mail/fields/common_address_field.rb +162 -0
  35. data/lib/mail/fields/common_date_field.rb +56 -0
  36. data/lib/mail/fields/common_field.rb +77 -0
  37. data/lib/mail/fields/common_message_id_field.rb +41 -0
  38. data/lib/mail/fields/content_description_field.rb +6 -14
  39. data/lib/mail/fields/content_disposition_field.rb +11 -38
  40. data/lib/mail/fields/content_id_field.rb +23 -51
  41. data/lib/mail/fields/content_location_field.rb +10 -25
  42. data/lib/mail/fields/content_transfer_encoding_field.rb +30 -31
  43. data/lib/mail/fields/content_type_field.rb +53 -84
  44. data/lib/mail/fields/date_field.rb +22 -52
  45. data/lib/mail/fields/from_field.rb +27 -49
  46. data/lib/mail/fields/in_reply_to_field.rb +37 -49
  47. data/lib/mail/fields/keywords_field.rb +17 -31
  48. data/lib/mail/fields/message_id_field.rb +24 -71
  49. data/lib/mail/fields/mime_version_field.rb +18 -30
  50. data/lib/mail/fields/named_structured_field.rb +10 -0
  51. data/lib/mail/fields/named_unstructured_field.rb +10 -0
  52. data/lib/mail/fields/optional_field.rb +9 -8
  53. data/lib/mail/fields/{common/parameter_hash.rb → parameter_hash.rb} +13 -11
  54. data/lib/mail/fields/received_field.rb +42 -57
  55. data/lib/mail/fields/references_field.rb +34 -49
  56. data/lib/mail/fields/reply_to_field.rb +27 -49
  57. data/lib/mail/fields/resent_bcc_field.rb +27 -49
  58. data/lib/mail/fields/resent_cc_field.rb +27 -49
  59. data/lib/mail/fields/resent_date_field.rb +4 -30
  60. data/lib/mail/fields/resent_from_field.rb +27 -49
  61. data/lib/mail/fields/resent_message_id_field.rb +4 -29
  62. data/lib/mail/fields/resent_sender_field.rb +26 -56
  63. data/lib/mail/fields/resent_to_field.rb +27 -49
  64. data/lib/mail/fields/return_path_field.rb +49 -54
  65. data/lib/mail/fields/sender_field.rb +33 -55
  66. data/lib/mail/fields/structured_field.rb +2 -30
  67. data/lib/mail/fields/subject_field.rb +8 -11
  68. data/lib/mail/fields/to_field.rb +27 -49
  69. data/lib/mail/fields/unstructured_field.rb +31 -47
  70. data/lib/mail/fields.rb +9 -0
  71. data/lib/mail/header.rb +71 -110
  72. data/lib/mail/mail.rb +34 -37
  73. data/lib/mail/matchers/attachment_matchers.rb +15 -0
  74. data/lib/mail/matchers/has_sent_mail.rb +21 -1
  75. data/lib/mail/message.rb +126 -127
  76. data/lib/mail/multibyte/chars.rb +24 -181
  77. data/lib/mail/multibyte/unicode.rb +11 -11
  78. data/lib/mail/multibyte/utils.rb +26 -43
  79. data/lib/mail/multibyte.rb +55 -16
  80. data/lib/mail/network/delivery_methods/exim.rb +8 -11
  81. data/lib/mail/network/delivery_methods/file_delivery.rb +15 -18
  82. data/lib/mail/network/delivery_methods/logger_delivery.rb +34 -0
  83. data/lib/mail/network/delivery_methods/sendmail.rb +32 -35
  84. data/lib/mail/network/delivery_methods/smtp.rb +125 -68
  85. data/lib/mail/network/delivery_methods/smtp_connection.rb +11 -16
  86. data/lib/mail/network/delivery_methods/test_mailer.rb +12 -13
  87. data/lib/mail/network/retriever_methods/base.rb +13 -13
  88. data/lib/mail/network/retriever_methods/imap.rb +25 -9
  89. data/lib/mail/network/retriever_methods/pop3.rb +25 -23
  90. data/lib/mail/network/retriever_methods/test_retriever.rb +3 -2
  91. data/lib/mail/network.rb +1 -0
  92. data/lib/mail/parser_tools.rb +15 -0
  93. data/lib/mail/parsers/address_lists_parser.rb +33228 -116
  94. data/lib/mail/parsers/address_lists_parser.rl +183 -0
  95. data/lib/mail/parsers/content_disposition_parser.rb +885 -49
  96. data/lib/mail/parsers/content_disposition_parser.rl +93 -0
  97. data/lib/mail/parsers/content_location_parser.rb +812 -23
  98. data/lib/mail/parsers/content_location_parser.rl +82 -0
  99. data/lib/mail/parsers/content_transfer_encoding_parser.rb +512 -21
  100. data/lib/mail/parsers/content_transfer_encoding_parser.rl +75 -0
  101. data/lib/mail/parsers/content_type_parser.rb +1039 -55
  102. data/lib/mail/parsers/content_type_parser.rl +94 -0
  103. data/lib/mail/parsers/date_time_parser.rb +880 -25
  104. data/lib/mail/parsers/date_time_parser.rl +73 -0
  105. data/lib/mail/parsers/envelope_from_parser.rb +3672 -40
  106. data/lib/mail/parsers/envelope_from_parser.rl +93 -0
  107. data/lib/mail/parsers/message_ids_parser.rb +5149 -25
  108. data/lib/mail/parsers/message_ids_parser.rl +97 -0
  109. data/lib/mail/parsers/mime_version_parser.rb +500 -26
  110. data/lib/mail/parsers/mime_version_parser.rl +72 -0
  111. data/lib/mail/parsers/phrase_lists_parser.rb +873 -22
  112. data/lib/mail/parsers/phrase_lists_parser.rl +94 -0
  113. data/lib/mail/parsers/received_parser.rb +8779 -43
  114. data/lib/mail/parsers/received_parser.rl +95 -0
  115. data/lib/mail/parsers/rfc2045_content_transfer_encoding.rl +13 -0
  116. data/lib/mail/parsers/rfc2045_content_type.rl +25 -0
  117. data/lib/mail/parsers/rfc2045_mime.rl +16 -0
  118. data/lib/mail/parsers/rfc2183_content_disposition.rl +15 -0
  119. data/lib/mail/parsers/rfc3629_utf8.rl +19 -0
  120. data/lib/mail/parsers/rfc5234_abnf_core_rules.rl +22 -0
  121. data/lib/mail/parsers/rfc5322.rl +74 -0
  122. data/lib/mail/parsers/rfc5322_address.rl +72 -0
  123. data/lib/mail/parsers/{ragel/date_time.rl → rfc5322_date_time.rl} +8 -1
  124. data/lib/mail/parsers/rfc5322_lexical_tokens.rl +60 -0
  125. data/lib/mail/parsers.rb +11 -25
  126. data/lib/mail/part.rb +25 -29
  127. data/lib/mail/parts_list.rb +62 -6
  128. data/lib/mail/smtp_envelope.rb +57 -0
  129. data/lib/mail/utilities.rb +361 -74
  130. data/lib/mail/version.rb +2 -2
  131. data/lib/mail/yaml.rb +30 -0
  132. data/lib/mail.rb +4 -37
  133. metadata +125 -67
  134. data/CHANGELOG.rdoc +0 -787
  135. data/CONTRIBUTING.md +0 -60
  136. data/Dependencies.txt +0 -2
  137. data/Gemfile +0 -11
  138. data/Rakefile +0 -29
  139. data/TODO.rdoc +0 -9
  140. data/lib/mail/check_delivery_params.rb +0 -21
  141. data/lib/mail/core_extensions/smtp.rb +0 -25
  142. data/lib/mail/core_extensions/string/access.rb +0 -146
  143. data/lib/mail/core_extensions/string/multibyte.rb +0 -79
  144. data/lib/mail/core_extensions/string.rb +0 -21
  145. data/lib/mail/fields/common/address_container.rb +0 -17
  146. data/lib/mail/fields/common/common_address.rb +0 -136
  147. data/lib/mail/fields/common/common_date.rb +0 -36
  148. data/lib/mail/fields/common/common_field.rb +0 -61
  149. data/lib/mail/fields/common/common_message_id.rb +0 -49
  150. data/lib/mail/multibyte/exceptions.rb +0 -9
  151. data/lib/mail/parsers/ragel/common.rl +0 -185
  152. data/lib/mail/parsers/ragel/parser_info.rb +0 -61
  153. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +0 -14864
  154. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +0 -37
  155. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +0 -751
  156. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +0 -37
  157. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +0 -614
  158. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +0 -37
  159. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +0 -447
  160. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +0 -37
  161. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +0 -825
  162. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +0 -37
  163. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +0 -817
  164. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +0 -37
  165. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +0 -2149
  166. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +0 -37
  167. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +0 -1570
  168. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +0 -37
  169. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +0 -440
  170. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +0 -37
  171. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +0 -564
  172. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +0 -37
  173. data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +0 -51
  174. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +0 -5144
  175. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +0 -37
  176. data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +0 -37
  177. data/lib/mail/parsers/ragel/ruby.rb +0 -40
  178. data/lib/mail/parsers/ragel.rb +0 -18
  179. data/lib/mail/version_specific/ruby_1_8.rb +0 -126
  180. data/lib/mail/version_specific/ruby_1_9.rb +0 -223
@@ -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.
@@ -38,53 +38,50 @@ module Mail
38
38
  #
39
39
  # mail.deliver!
40
40
  class Sendmail
41
- include Mail::CheckDeliveryParams
41
+ DEFAULTS = {
42
+ :location => '/usr/sbin/sendmail',
43
+ :arguments => %w[ -i ]
44
+ }
45
+
46
+ attr_accessor :settings
47
+
48
+ class DeliveryError < StandardError
49
+ end
42
50
 
43
51
  def initialize(values)
44
- self.settings = { :location => '/usr/sbin/sendmail',
45
- :arguments => '-i' }.merge(values)
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)
46
54
  end
47
55
 
48
- attr_accessor :settings
56
+ def destinations_for(envelope)
57
+ envelope.to
58
+ end
49
59
 
50
60
  def deliver!(mail)
51
- smtp_from, smtp_to, message = check_delivery_params(mail)
61
+ envelope = Mail::SmtpEnvelope.new(mail)
52
62
 
53
- from = "-f #{self.class.shellquote(smtp_from)}"
54
- 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
55
66
 
56
- arguments = "#{settings[:arguments]} #{from} --"
57
- self.class.call(settings[:location], arguments, to, message)
58
- end
67
+ if destinations = destinations_for(envelope)
68
+ command.push '--'
69
+ command.concat destinations
70
+ end
59
71
 
60
- def self.call(path, arguments, destinations, encoded_message)
61
- popen "#{path} #{arguments} #{destinations}" do |io|
62
- io.puts ::Mail::Utilities.to_lf(encoded_message)
72
+ popen(command) do |io|
73
+ io.puts ::Mail::Utilities.binary_unsafe_to_lf(envelope.message)
63
74
  io.flush
64
75
  end
65
76
  end
66
77
 
67
- if RUBY_VERSION < '1.9.0'
68
- def self.popen(command, &block)
69
- IO.popen "#{command} 2>&1", 'w+', &block
70
- end
71
- else
72
- def self.popen(command, &block)
73
- 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
74
85
  end
75
- end
76
-
77
- # The following is an adaptation of ruby 1.9.2's shellwords.rb file,
78
- # it is modified to include '+' in the allowed list to allow for
79
- # sendmail to accept email addresses as the sender with a + in them.
80
- def self.shellquote(address)
81
- # Process as a single byte sequence because not all shell
82
- # implementations are multibyte aware.
83
- #
84
- # A LF cannot be escaped with a backslash because a backslash + LF
85
- # combo is regarded as line continuation and simply ignored. Strip it.
86
- escaped = address.gsub(/([^A-Za-z0-9_\s\+\-.,:\/@])/n, "\\\\\\1").gsub("\n", '')
87
- %("#{escaped}")
88
- end
89
86
  end
90
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
@@ -45,99 +53,148 @@ module Mail
45
53
  # hostname or update the certificate authorities trusted by your ruby. If
46
54
  # that isn't possible, you can control this behavior with
47
55
  # an :openssl_verify_mode setting. Its value may be either an OpenSSL
48
- # verify mode constant (OpenSSL::SSL::VERIFY_NONE), or a string containing
49
- # the name of an OpenSSL verify mode (none, peer, client_once,
50
- # fail_if_no_peer_cert).
56
+ # verify mode constant (OpenSSL::SSL::VERIFY_NONE, OpenSSL::SSL::VERIFY_PEER),
57
+ # or a string containing the name of an OpenSSL verify mode (none, peer).
58
+ #
59
+ # === Others
51
60
  #
52
- # === Others
53
- #
54
61
  # Feel free to send me other examples that were tricky
55
- #
62
+ #
56
63
  # === Delivering the email
57
- #
64
+ #
58
65
  # Once you have the settings right, sending the email is done by:
59
- #
66
+ #
60
67
  # Mail.deliver do
61
68
  # to 'mikel@test.lindsaar.net'
62
69
  # from 'ada@test.lindsaar.net'
63
70
  # subject 'testing sendmail'
64
71
  # body 'testing sendmail'
65
72
  # end
66
- #
73
+ #
67
74
  # Or by calling deliver on a Mail message
68
- #
75
+ #
69
76
  # mail = Mail.new do
70
77
  # to 'mikel@test.lindsaar.net'
71
78
  # from 'ada@test.lindsaar.net'
72
79
  # subject 'testing sendmail'
73
80
  # body 'testing sendmail'
74
81
  # end
75
- #
82
+ #
76
83
  # mail.deliver!
77
84
  class SMTP
78
- include Mail::CheckDeliveryParams
85
+ attr_accessor :settings
86
+
87
+ DEFAULTS = {
88
+ :address => 'localhost',
89
+ :port => 25,
90
+ :domain => 'localhost.localdomain',
91
+ :user_name => nil,
92
+ :password => nil,
93
+ :authentication => nil,
94
+ :enable_starttls => nil,
95
+ :enable_starttls_auto => nil,
96
+ :openssl_verify_mode => nil,
97
+ :ssl => nil,
98
+ :tls => nil,
99
+ :open_timeout => 5,
100
+ :read_timeout => 5
101
+ }
79
102
 
80
103
  def initialize(values)
81
- self.settings = { :address => "localhost",
82
- :port => 25,
83
- :domain => 'localhost.localdomain',
84
- :user_name => nil,
85
- :password => nil,
86
- :authentication => nil,
87
- :enable_starttls_auto => true,
88
- :openssl_verify_mode => nil,
89
- :ssl => nil,
90
- :tls => nil
91
- }.merge!(values)
104
+ self.settings = DEFAULTS.merge(values)
92
105
  end
93
106
 
94
- attr_accessor :settings
95
-
96
- # Send the message via SMTP.
97
- # The from and to attributes are optional. If not set, they are retrieve from the Message.
98
107
  def deliver!(mail)
99
- smtp_from, smtp_to, message = check_delivery_params(mail)
108
+ response = start_smtp_session do |smtp|
109
+ Mail::SMTPConnection.new(:connection => smtp, :return_response => true).deliver!(mail)
110
+ end
100
111
 
101
- smtp = Net::SMTP.new(settings[:address], settings[:port])
102
- if settings[:tls] || settings[:ssl]
103
- if smtp.respond_to?(:enable_tls)
104
- smtp.enable_tls(ssl_context)
105
- end
106
- elsif settings[:enable_starttls_auto]
107
- if smtp.respond_to?(:enable_starttls_auto)
108
- smtp.enable_starttls_auto(ssl_context)
112
+ settings[:return_response] ? response : self
113
+ end
114
+
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
109
146
  end
110
147
  end
111
148
 
112
- response = nil
113
- smtp.start(settings[:domain], settings[:user_name], settings[:password], settings[:authentication]) do |smtp_obj|
114
- response = smtp_obj.sendmail(message, smtp_from, smtp_to)
149
+ def smtp_tls?
150
+ (setting_provided?(:tls) && settings[:tls]) || (setting_provided?(:ssl) && settings[:ssl])
115
151
  end
116
152
 
117
- if settings[:return_response]
118
- response
119
- else
120
- self
153
+ def start_smtp_session(&block)
154
+ build_smtp_session.start(settings[:domain], settings[:user_name], settings[:password], settings[:authentication], &block)
121
155
  end
122
- end
123
-
124
156
 
125
- private
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
+
162
+ Net::SMTP.new(settings[:address], settings[:port]).tap do |smtp|
163
+ if smtp_tls?
164
+ smtp.disable_starttls
165
+ smtp.enable_tls(ssl_context)
166
+ else
167
+ smtp.disable_tls
126
168
 
127
- # Allow SSL context to be configured via settings, for Ruby >= 1.9
128
- # Just returns openssl verify mode for Ruby 1.8.x
129
- def ssl_context
130
- openssl_verify_mode = settings[:openssl_verify_mode]
169
+ case smtp_starttls
170
+ when :always
171
+ smtp.enable_starttls(ssl_context)
172
+ when :auto
173
+ smtp.enable_starttls_auto(ssl_context)
174
+ else
175
+ smtp.disable_starttls
176
+ end
177
+ end
131
178
 
132
- if openssl_verify_mode.kind_of?(String)
133
- openssl_verify_mode = "OpenSSL::SSL::VERIFY_#{openssl_verify_mode.upcase}".constantize
179
+ smtp.open_timeout = settings[:open_timeout] if settings[:open_timeout]
180
+ smtp.read_timeout = settings[:read_timeout] if settings[:read_timeout]
181
+ end
134
182
  end
135
183
 
136
- context = Net::SMTP.default_ssl_context
137
- context.verify_mode = openssl_verify_mode
138
- context.ca_path = settings[:ca_path] if settings[:ca_path]
139
- context.ca_file = settings[:ca_file] if settings[:ca_file]
140
- context
141
- end
184
+ # Allow SSL context to be configured via settings, for Ruby >= 1.9
185
+ # Just returns openssl verify mode for Ruby 1.8.x
186
+ def ssl_context
187
+ openssl_verify_mode = settings[:openssl_verify_mode]
188
+
189
+ if openssl_verify_mode.kind_of?(String)
190
+ openssl_verify_mode = OpenSSL::SSL.const_get("VERIFY_#{openssl_verify_mode.upcase}")
191
+ end
192
+
193
+ context = Net::SMTP.default_ssl_context
194
+ context.verify_mode = openssl_verify_mode if openssl_verify_mode
195
+ context.ca_path = settings[:ca_path] if settings[:ca_path]
196
+ context.ca_file = settings[:ca_file] if settings[:ca_file]
197
+ context
198
+ end
142
199
  end
143
200
  end
@@ -1,44 +1,44 @@
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
- include Mail::CheckDeliveryParams
41
+ attr_accessor :smtp, :settings
42
42
 
43
43
  def initialize(values)
44
44
  raise ArgumentError.new('A Net::SMTP object is required for this delivery method') if values[:connection].nil?
@@ -46,17 +46,12 @@ module Mail
46
46
  self.settings = values
47
47
  end
48
48
 
49
- attr_accessor :smtp
50
- attr_accessor :settings
51
-
52
49
  # Send the message via SMTP.
53
50
  # The from and to attributes are optional. If not set, they are retrieve from the Message.
54
51
  def deliver!(mail)
55
- smtp_from, smtp_to, message = check_delivery_params(mail)
56
- response = smtp.sendmail(message, smtp_from, smtp_to)
57
-
52
+ envelope = Mail::SmtpEnvelope.new(mail)
53
+ response = smtp.sendmail(envelope.message, envelope.from, envelope.to)
58
54
  settings[:return_response] ? response : self
59
55
  end
60
-
61
56
  end
62
57
  end
@@ -1,45 +1,44 @@
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
11
- include Mail::CheckDeliveryParams
12
-
13
11
  # Provides a store of all the emails sent with the TestMailer so you can check them.
14
- def TestMailer.deliveries
12
+ def self.deliveries
15
13
  @@deliveries ||= []
16
14
  end
17
15
 
18
16
  # Allows you to over write the default deliveries store from an array to some
19
- # other object. If you just want to clear the store,
17
+ # other object. If you just want to clear the store,
20
18
  # call TestMailer.deliveries.clear.
21
- #
19
+ #
22
20
  # If you place another object here, please make sure it responds to:
23
- #
21
+ #
24
22
  # * << (message)
25
23
  # * clear
26
24
  # * length
27
25
  # * size
28
26
  # * and other common Array methods
29
- def TestMailer.deliveries=(val)
27
+ def self.deliveries=(val)
30
28
  @@deliveries = val
31
29
  end
32
30
 
31
+ attr_accessor :settings
32
+
33
33
  def initialize(values)
34
34
  @settings = values.dup
35
35
  end
36
-
37
- attr_accessor :settings
38
36
 
39
37
  def deliver!(mail)
40
- check_delivery_params(mail)
38
+ # Create the envelope to validate it
39
+ Mail::SmtpEnvelope.new(mail)
40
+
41
41
  Mail::TestMailer.deliveries << mail
42
42
  end
43
-
44
43
  end
45
44
  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
 
@@ -29,7 +29,7 @@ module Mail
29
29
  # order: order of emails returned. Possible values are :asc or :desc. Default value is :asc.
30
30
  # count: number of emails to retrieve. The default value is 10. A value of 1 returns an
31
31
  # instance of Message, not an array of Message instances.
32
- # keys: are passed as criteria to the SEARCH command. They can either be a string holding the entire search string,
32
+ # keys: are passed as criteria to the SEARCH command. They can either be a string holding the entire search string,
33
33
  # or a single-dimension array of search keywords and arguments. Refer to [IMAP] section 6.4.4 for a full list
34
34
  # The default is 'ALL'
35
35
  #
@@ -38,14 +38,15 @@ module Mail
38
38
  #
39
39
  class IMAP < Retriever
40
40
  require 'net/imap' unless defined?(Net::IMAP)
41
-
41
+
42
42
  def initialize(values)
43
43
  self.settings = { :address => "localhost",
44
44
  :port => 143,
45
45
  :user_name => nil,
46
46
  :password => nil,
47
47
  :authentication => nil,
48
- :enable_ssl => false }.merge!(values)
48
+ :enable_ssl => false,
49
+ :enable_starttls => false }.merge!(values)
49
50
  end
50
51
 
51
52
  attr_accessor :settings
@@ -64,17 +65,20 @@ module Mail
64
65
  # This is helpful when you don't want your messages to be set to read automatically. Default is false.
65
66
  # delete_after_find: flag for whether to delete each retreived email after find. Default
66
67
  # is false. Use #find_and_delete if you would like this to default to true.
67
- # keys: are passed as criteria to the SEARCH command. They can either be a string holding the entire search string,
68
+ # keys: are passed as criteria to the SEARCH command. They can either be a string holding the entire search string,
68
69
  # or a single-dimension array of search keywords and arguments. Refer to [IMAP] section 6.4.4 for a full list
69
70
  # The default is 'ALL'
71
+ # search_charset: charset to pass to IMAP server search. Omitted by default. Example: 'UTF-8' or 'ASCII'.
70
72
  #
71
- def find(options={}, &block)
73
+ def find(options=nil, &block)
72
74
  options = validate_options(options)
73
75
 
74
76
  start do |imap|
75
77
  options[:read_only] ? imap.examine(options[:mailbox]) : imap.select(options[:mailbox])
76
78
 
77
- uids = imap.uid_search(options[:keys])
79
+ uids = imap.uid_search(options[:keys], options[:search_charset])
80
+ .to_a # older net-imap may return nil, newer may return ESearchResult struct
81
+ .sort # RFC3501 does _not_ require UIDs to be returned in order
78
82
  uids.reverse! if options[:what].to_sym == :last
79
83
  uids = uids.first(options[:count]) if options[:count].is_a?(Integer)
80
84
  uids.reverse! if (options[:what].to_sym == :last && options[:order].to_sym == :asc) ||
@@ -83,14 +87,18 @@ module Mail
83
87
  if block_given?
84
88
  uids.each do |uid|
85
89
  uid = options[:uid].to_i unless options[:uid].nil?
86
- fetchdata = imap.uid_fetch(uid, ['RFC822'])[0]
90
+ fetchdata = imap.uid_fetch(uid, ['RFC822', 'FLAGS'])[0]
87
91
  new_message = Mail.new(fetchdata.attr['RFC822'])
88
92
  new_message.mark_for_delete = true if options[:delete_after_find]
89
- if block.arity == 3
93
+
94
+ if block.arity == 4
95
+ yield new_message, imap, uid, fetchdata.attr['FLAGS']
96
+ elsif block.arity == 3
90
97
  yield new_message, imap, uid
91
98
  else
92
99
  yield new_message
93
100
  end
101
+
94
102
  imap.uid_store(uid, "+FLAGS", [Net::IMAP::DELETED]) if options[:delete_after_find] && new_message.is_marked_for_delete?
95
103
  break unless options[:uid].nil?
96
104
  end
@@ -116,6 +124,7 @@ module Mail
116
124
  mailbox = Net::IMAP.encode_utf7(mailbox)
117
125
 
118
126
  start do |imap|
127
+ imap.select(mailbox)
119
128
  imap.uid_search(['ALL']).each do |uid|
120
129
  imap.uid_store(uid, "+FLAGS", [Net::IMAP::DELETED])
121
130
  end
@@ -136,7 +145,7 @@ module Mail
136
145
 
137
146
  # Set default options
138
147
  def validate_options(options)
139
- options ||= {}
148
+ options = options ? Hash[options] : {}
140
149
  options[:mailbox] ||= 'INBOX'
141
150
  options[:count] ||= 10
142
151
  options[:order] ||= :asc
@@ -154,7 +163,14 @@ module Mail
154
163
  def start(config=Mail::Configuration.instance, &block)
155
164
  raise ArgumentError.new("Mail::Retrievable#imap_start takes a block") unless block_given?
156
165
 
166
+ if settings[:enable_starttls] && settings[:enable_ssl]
167
+ raise ArgumentError, ":enable_starttls and :enable_ssl are mutually exclusive. Set :enable_ssl if you're on an IMAPS connection. Set :enable_starttls if you're on an IMAP connection and using STARTTLS for secure TLS upgrade."
168
+ end
169
+
157
170
  imap = Net::IMAP.new(settings[:address], settings[:port], settings[:enable_ssl], nil, false)
171
+
172
+ imap.starttls if settings[:enable_starttls]
173
+
158
174
  if settings[:authentication].nil?
159
175
  imap.login(settings[:user_name], settings[:password])
160
176
  else