mail 2.5.5 → 2.8.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 (191) hide show
  1. checksums.yaml +5 -5
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +170 -108
  4. data/lib/mail/attachments_list.rb +13 -10
  5. data/lib/mail/body.rb +105 -91
  6. data/lib/mail/check_delivery_params.rb +30 -22
  7. data/lib/mail/configuration.rb +3 -0
  8. data/lib/mail/constants.rb +79 -0
  9. data/lib/mail/elements/address.rb +118 -174
  10. data/lib/mail/elements/address_list.rb +16 -56
  11. data/lib/mail/elements/content_disposition_element.rb +12 -22
  12. data/lib/mail/elements/content_location_element.rb +9 -17
  13. data/lib/mail/elements/content_transfer_encoding_element.rb +8 -19
  14. data/lib/mail/elements/content_type_element.rb +20 -30
  15. data/lib/mail/elements/date_time_element.rb +10 -21
  16. data/lib/mail/elements/envelope_from_element.rb +23 -31
  17. data/lib/mail/elements/message_ids_element.rb +22 -20
  18. data/lib/mail/elements/mime_version_element.rb +10 -21
  19. data/lib/mail/elements/phrase_list.rb +13 -15
  20. data/lib/mail/elements/received_element.rb +26 -21
  21. data/lib/mail/elements.rb +1 -0
  22. data/lib/mail/encodings/7bit.rb +10 -14
  23. data/lib/mail/encodings/8bit.rb +5 -18
  24. data/lib/mail/encodings/base64.rb +15 -10
  25. data/lib/mail/encodings/binary.rb +4 -22
  26. data/lib/mail/encodings/identity.rb +24 -0
  27. data/lib/mail/encodings/quoted_printable.rb +13 -7
  28. data/lib/mail/encodings/transfer_encoding.rb +47 -28
  29. data/lib/mail/encodings/unix_to_unix.rb +20 -0
  30. data/lib/mail/encodings.rb +102 -93
  31. data/lib/mail/envelope.rb +12 -19
  32. data/lib/mail/field.rb +143 -71
  33. data/lib/mail/field_list.rb +73 -19
  34. data/lib/mail/fields/bcc_field.rb +42 -48
  35. data/lib/mail/fields/cc_field.rb +29 -50
  36. data/lib/mail/fields/comments_field.rb +28 -37
  37. data/lib/mail/fields/common_address_field.rb +170 -0
  38. data/lib/mail/fields/common_date_field.rb +58 -0
  39. data/lib/mail/fields/common_field.rb +77 -0
  40. data/lib/mail/fields/common_message_id_field.rb +42 -0
  41. data/lib/mail/fields/content_description_field.rb +8 -14
  42. data/lib/mail/fields/content_disposition_field.rb +20 -44
  43. data/lib/mail/fields/content_id_field.rb +25 -51
  44. data/lib/mail/fields/content_location_field.rb +12 -25
  45. data/lib/mail/fields/content_transfer_encoding_field.rb +31 -36
  46. data/lib/mail/fields/content_type_field.rb +51 -80
  47. data/lib/mail/fields/date_field.rb +24 -52
  48. data/lib/mail/fields/from_field.rb +29 -50
  49. data/lib/mail/fields/in_reply_to_field.rb +39 -49
  50. data/lib/mail/fields/keywords_field.rb +19 -32
  51. data/lib/mail/fields/message_id_field.rb +26 -71
  52. data/lib/mail/fields/mime_version_field.rb +20 -30
  53. data/lib/mail/fields/named_structured_field.rb +11 -0
  54. data/lib/mail/fields/named_unstructured_field.rb +11 -0
  55. data/lib/mail/fields/optional_field.rb +10 -7
  56. data/lib/mail/fields/{common/parameter_hash.rb → parameter_hash.rb} +16 -13
  57. data/lib/mail/fields/received_field.rb +44 -57
  58. data/lib/mail/fields/references_field.rb +36 -49
  59. data/lib/mail/fields/reply_to_field.rb +29 -50
  60. data/lib/mail/fields/resent_bcc_field.rb +29 -50
  61. data/lib/mail/fields/resent_cc_field.rb +29 -50
  62. data/lib/mail/fields/resent_date_field.rb +6 -30
  63. data/lib/mail/fields/resent_from_field.rb +29 -50
  64. data/lib/mail/fields/resent_message_id_field.rb +6 -29
  65. data/lib/mail/fields/resent_sender_field.rb +28 -57
  66. data/lib/mail/fields/resent_to_field.rb +29 -50
  67. data/lib/mail/fields/return_path_field.rb +51 -55
  68. data/lib/mail/fields/sender_field.rb +35 -56
  69. data/lib/mail/fields/structured_field.rb +4 -30
  70. data/lib/mail/fields/subject_field.rb +10 -11
  71. data/lib/mail/fields/to_field.rb +29 -50
  72. data/lib/mail/fields/unstructured_field.rb +43 -51
  73. data/lib/mail/fields.rb +1 -0
  74. data/lib/mail/header.rb +78 -129
  75. data/lib/mail/indifferent_hash.rb +1 -0
  76. data/lib/mail/mail.rb +18 -11
  77. data/lib/mail/matchers/attachment_matchers.rb +44 -0
  78. data/lib/mail/matchers/has_sent_mail.rb +81 -4
  79. data/lib/mail/message.rb +142 -139
  80. data/lib/mail/multibyte/chars.rb +24 -180
  81. data/lib/mail/multibyte/unicode.rb +32 -27
  82. data/lib/mail/multibyte/utils.rb +27 -43
  83. data/lib/mail/multibyte.rb +56 -16
  84. data/lib/mail/network/delivery_methods/exim.rb +6 -4
  85. data/lib/mail/network/delivery_methods/file_delivery.rb +12 -10
  86. data/lib/mail/network/delivery_methods/logger_delivery.rb +34 -0
  87. data/lib/mail/network/delivery_methods/sendmail.rb +63 -21
  88. data/lib/mail/network/delivery_methods/smtp.rb +76 -50
  89. data/lib/mail/network/delivery_methods/smtp_connection.rb +4 -4
  90. data/lib/mail/network/delivery_methods/test_mailer.rb +5 -2
  91. data/lib/mail/network/retriever_methods/base.rb +9 -8
  92. data/lib/mail/network/retriever_methods/imap.rb +37 -18
  93. data/lib/mail/network/retriever_methods/pop3.rb +6 -3
  94. data/lib/mail/network/retriever_methods/test_retriever.rb +4 -2
  95. data/lib/mail/network.rb +2 -0
  96. data/lib/mail/parser_tools.rb +15 -0
  97. data/lib/mail/parsers/address_lists_parser.rb +33242 -0
  98. data/lib/mail/parsers/address_lists_parser.rl +179 -0
  99. data/lib/mail/parsers/content_disposition_parser.rb +901 -0
  100. data/lib/mail/parsers/content_disposition_parser.rl +89 -0
  101. data/lib/mail/parsers/content_location_parser.rb +822 -0
  102. data/lib/mail/parsers/content_location_parser.rl +78 -0
  103. data/lib/mail/parsers/content_transfer_encoding_parser.rb +522 -0
  104. data/lib/mail/parsers/content_transfer_encoding_parser.rl +71 -0
  105. data/lib/mail/parsers/content_type_parser.rb +1048 -0
  106. data/lib/mail/parsers/content_type_parser.rl +90 -0
  107. data/lib/mail/parsers/date_time_parser.rb +891 -0
  108. data/lib/mail/parsers/date_time_parser.rl +69 -0
  109. data/lib/mail/parsers/envelope_from_parser.rb +3675 -0
  110. data/lib/mail/parsers/envelope_from_parser.rl +89 -0
  111. data/lib/mail/parsers/message_ids_parser.rb +5161 -0
  112. data/lib/mail/parsers/message_ids_parser.rl +93 -0
  113. data/lib/mail/parsers/mime_version_parser.rb +513 -0
  114. data/lib/mail/parsers/mime_version_parser.rl +68 -0
  115. data/lib/mail/parsers/phrase_lists_parser.rb +884 -0
  116. data/lib/mail/parsers/phrase_lists_parser.rl +90 -0
  117. data/lib/mail/parsers/received_parser.rb +8782 -0
  118. data/lib/mail/parsers/received_parser.rl +91 -0
  119. data/lib/mail/parsers/rfc2045_content_transfer_encoding.rl +13 -0
  120. data/lib/mail/parsers/rfc2045_content_type.rl +25 -0
  121. data/lib/mail/parsers/rfc2045_mime.rl +16 -0
  122. data/lib/mail/parsers/rfc2183_content_disposition.rl +15 -0
  123. data/lib/mail/parsers/rfc3629_utf8.rl +19 -0
  124. data/lib/mail/parsers/rfc5234_abnf_core_rules.rl +22 -0
  125. data/lib/mail/parsers/rfc5322.rl +74 -0
  126. data/lib/mail/parsers/rfc5322_address.rl +72 -0
  127. data/lib/mail/parsers/rfc5322_date_time.rl +37 -0
  128. data/lib/mail/parsers/rfc5322_lexical_tokens.rl +60 -0
  129. data/lib/mail/parsers.rb +13 -0
  130. data/lib/mail/part.rb +11 -12
  131. data/lib/mail/parts_list.rb +90 -14
  132. data/lib/mail/smtp_envelope.rb +57 -0
  133. data/lib/mail/utilities.rb +415 -76
  134. data/lib/mail/values/unicode_tables.dat +0 -0
  135. data/lib/mail/version.rb +8 -15
  136. data/lib/mail/yaml.rb +30 -0
  137. data/lib/mail.rb +9 -32
  138. metadata +127 -79
  139. data/CHANGELOG.rdoc +0 -742
  140. data/CONTRIBUTING.md +0 -45
  141. data/Dependencies.txt +0 -3
  142. data/Gemfile +0 -32
  143. data/Rakefile +0 -21
  144. data/TODO.rdoc +0 -9
  145. data/lib/VERSION +0 -4
  146. data/lib/load_parsers.rb +0 -35
  147. data/lib/mail/core_extensions/nil.rb +0 -19
  148. data/lib/mail/core_extensions/object.rb +0 -13
  149. data/lib/mail/core_extensions/smtp.rb +0 -24
  150. data/lib/mail/core_extensions/string/access.rb +0 -145
  151. data/lib/mail/core_extensions/string/multibyte.rb +0 -78
  152. data/lib/mail/core_extensions/string.rb +0 -33
  153. data/lib/mail/fields/common/address_container.rb +0 -16
  154. data/lib/mail/fields/common/common_address.rb +0 -140
  155. data/lib/mail/fields/common/common_date.rb +0 -42
  156. data/lib/mail/fields/common/common_field.rb +0 -57
  157. data/lib/mail/fields/common/common_message_id.rb +0 -48
  158. data/lib/mail/multibyte/exceptions.rb +0 -8
  159. data/lib/mail/parsers/address_lists.rb +0 -64
  160. data/lib/mail/parsers/address_lists.treetop +0 -19
  161. data/lib/mail/parsers/content_disposition.rb +0 -535
  162. data/lib/mail/parsers/content_disposition.treetop +0 -46
  163. data/lib/mail/parsers/content_location.rb +0 -139
  164. data/lib/mail/parsers/content_location.treetop +0 -20
  165. data/lib/mail/parsers/content_transfer_encoding.rb +0 -201
  166. data/lib/mail/parsers/content_transfer_encoding.treetop +0 -18
  167. data/lib/mail/parsers/content_type.rb +0 -971
  168. data/lib/mail/parsers/content_type.treetop +0 -68
  169. data/lib/mail/parsers/date_time.rb +0 -114
  170. data/lib/mail/parsers/date_time.treetop +0 -11
  171. data/lib/mail/parsers/envelope_from.rb +0 -194
  172. data/lib/mail/parsers/envelope_from.treetop +0 -32
  173. data/lib/mail/parsers/message_ids.rb +0 -45
  174. data/lib/mail/parsers/message_ids.treetop +0 -15
  175. data/lib/mail/parsers/mime_version.rb +0 -144
  176. data/lib/mail/parsers/mime_version.treetop +0 -19
  177. data/lib/mail/parsers/phrase_lists.rb +0 -45
  178. data/lib/mail/parsers/phrase_lists.treetop +0 -15
  179. data/lib/mail/parsers/received.rb +0 -71
  180. data/lib/mail/parsers/received.treetop +0 -11
  181. data/lib/mail/parsers/rfc2045.rb +0 -421
  182. data/lib/mail/parsers/rfc2045.treetop +0 -35
  183. data/lib/mail/parsers/rfc2822.rb +0 -5397
  184. data/lib/mail/parsers/rfc2822.treetop +0 -408
  185. data/lib/mail/parsers/rfc2822_obsolete.rb +0 -3768
  186. data/lib/mail/parsers/rfc2822_obsolete.treetop +0 -241
  187. data/lib/mail/patterns.rb +0 -35
  188. data/lib/mail/version_specific/ruby_1_8.rb +0 -119
  189. data/lib/mail/version_specific/ruby_1_9.rb +0 -147
  190. data/lib/tasks/corpus.rake +0 -125
  191. data/lib/tasks/treetop.rake +0 -10
@@ -0,0 +1,34 @@
1
+ require 'mail/smtp_envelope'
2
+
3
+ module Mail
4
+ class LoggerDelivery
5
+ attr_reader :logger, :severity, :settings
6
+
7
+ def initialize(settings)
8
+ @settings = settings
9
+ @logger = settings.fetch(:logger) { default_logger }
10
+ @severity = derive_severity(settings[:severity])
11
+ end
12
+
13
+ def deliver!(mail)
14
+ logger.log(severity) { Mail::SmtpEnvelope.new(mail).message }
15
+ end
16
+
17
+ private
18
+ def default_logger
19
+ require 'logger'
20
+ ::Logger.new($stdout)
21
+ end
22
+
23
+ def derive_severity(severity)
24
+ case severity
25
+ when nil
26
+ Logger::INFO
27
+ when Integer
28
+ severity
29
+ else
30
+ Logger.const_get(severity.to_s.upcase)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -1,4 +1,5 @@
1
- require 'mail/check_delivery_params'
1
+ # frozen_string_literal: true
2
+ require 'mail/smtp_envelope'
2
3
 
3
4
  module Mail
4
5
  # A delivery method implementation which sends via sendmail.
@@ -39,53 +40,94 @@ module Mail
39
40
  class Sendmail
40
41
  DEFAULTS = {
41
42
  :location => '/usr/sbin/sendmail',
42
- :arguments => '-i'
43
+ :arguments => %w[ -i ]
43
44
  }
44
45
 
45
46
  attr_accessor :settings
46
47
 
48
+ class DeliveryError < StandardError
49
+ end
50
+
47
51
  def initialize(values)
52
+ if values[:arguments].is_a?(String)
53
+ deprecation_warn.call \
54
+ 'Initializing Mail::Sendmail with :arguments of type String is deprecated.' \
55
+ ' Instead ensure :arguments is an array of strings, e.g. ["-i", "-t"]'
56
+ end
48
57
  self.settings = self.class::DEFAULTS.merge(values)
49
58
  end
50
59
 
60
+ def destinations_for(envelope)
61
+ envelope.to
62
+ end
63
+
51
64
  def deliver!(mail)
52
- smtp_from, smtp_to, message = Mail::CheckDeliveryParams.check(mail)
65
+ envelope = Mail::SmtpEnvelope.new(mail)
53
66
 
54
- from = "-f #{self.class.shellquote(smtp_from)}"
55
- to = smtp_to.map { |to| self.class.shellquote(to) }.join(' ')
67
+ arguments = settings[:arguments]
68
+ if arguments.is_a? String
69
+ return old_deliver(envelope)
70
+ end
56
71
 
57
- arguments = "#{settings[:arguments]} #{from} --"
58
- self.class.call(settings[:location], arguments, to, message)
59
- end
72
+ command = [settings[:location]]
73
+ command.concat Array(arguments)
74
+ command.concat [ '-f', envelope.from ] if envelope.from
60
75
 
61
- def self.call(path, arguments, destinations, encoded_message)
62
- popen "#{path} #{arguments} #{destinations}" do |io|
63
- io.puts encoded_message.to_lf
76
+ if destinations = destinations_for(envelope)
77
+ command.push '--'
78
+ command.concat destinations
79
+ end
80
+
81
+ popen(command) do |io|
82
+ io.puts ::Mail::Utilities.binary_unsafe_to_lf(envelope.message)
64
83
  io.flush
65
84
  end
66
85
  end
67
86
 
68
- if RUBY_VERSION < '1.9.0'
69
- def self.popen(command, &block)
70
- IO.popen "#{command} 2>&1", 'w+', &block
87
+ private
88
+ def popen(command, &block)
89
+ IO.popen(command, 'w+', :err => :out, &block).tap do
90
+ if $?.exitstatus != 0
91
+ raise DeliveryError, "Delivery failed with exitstatus #{$?.exitstatus}: #{command.inspect}"
92
+ end
93
+ end
71
94
  end
72
- else
73
- def self.popen(command, &block)
74
- IO.popen command, 'w+', :err => :out, &block
95
+
96
+ #+ support for delivery using string arguments (deprecated)
97
+ def old_deliver(envelope)
98
+ smtp_from = envelope.from
99
+ smtp_to = destinations_for(envelope)
100
+
101
+ from = "-f #{shellquote(smtp_from)}" if smtp_from
102
+ destination = smtp_to.map { |to| shellquote(to) }.join(' ')
103
+
104
+ arguments = "#{settings[:arguments]} #{from} --"
105
+ command = "#{settings[:location]} #{arguments} #{destination}"
106
+ popen command do |io|
107
+ io.puts ::Mail::Utilities.binary_unsafe_to_lf(envelope.message)
108
+ io.flush
75
109
  end
76
110
  end
77
111
 
78
112
  # The following is an adaptation of ruby 1.9.2's shellwords.rb file,
79
- # it is modified to include '+' in the allowed list to allow for
80
- # sendmail to accept email addresses as the sender with a + in them.
81
- def self.shellquote(address)
113
+ # with the following modifications:
114
+ #
115
+ # - Wraps in double quotes
116
+ # - Allows '+' to accept email addresses with them
117
+ # - Allows '~' as it is not unescaped in double quotes
118
+ def shellquote(address)
82
119
  # Process as a single byte sequence because not all shell
83
120
  # implementations are multibyte aware.
84
121
  #
85
122
  # A LF cannot be escaped with a backslash because a backslash + LF
86
123
  # combo is regarded as line continuation and simply ignored. Strip it.
87
- escaped = address.gsub(/([^A-Za-z0-9_\s\+\-.,:\/@])/n, "\\\\\\1").gsub("\n", '')
124
+ escaped = address.gsub(/([^A-Za-z0-9_\s\+\-.,:\/@~])/n, "\\\\\\1").gsub("\n", '')
88
125
  %("#{escaped}")
89
126
  end
127
+ #- support for delivery using string arguments
128
+
129
+ def deprecation_warn
130
+ defined?(ActiveSupport::Deprecation.warn) ? ActiveSupport::Deprecation.method(:warn) : Kernel.method(:warn)
131
+ end
90
132
  end
91
133
  end
@@ -1,4 +1,5 @@
1
- require 'mail/check_delivery_params'
1
+ # frozen_string_literal: true
2
+ require 'mail/smtp_envelope'
2
3
 
3
4
  module Mail
4
5
  # == Sending Email with SMTP
@@ -44,9 +45,8 @@ module Mail
44
45
  # hostname or update the certificate authorities trusted by your ruby. If
45
46
  # that isn't possible, you can control this behavior with
46
47
  # an :openssl_verify_mode setting. Its value may be either an OpenSSL
47
- # verify mode constant (OpenSSL::SSL::VERIFY_NONE), or a string containing
48
- # the name of an OpenSSL verify mode (none, peer, client_once,
49
- # fail_if_no_peer_cert).
48
+ # verify mode constant (OpenSSL::SSL::VERIFY_NONE, OpenSSL::SSL::VERIFY_PEER),
49
+ # or a string containing the name of an OpenSSL verify mode (none, peer).
50
50
  #
51
51
  # === Others
52
52
  #
@@ -76,64 +76,90 @@ module Mail
76
76
  class SMTP
77
77
  attr_accessor :settings
78
78
 
79
+ DEFAULTS = {
80
+ :address => 'localhost',
81
+ :port => 25,
82
+ :domain => 'localhost.localdomain',
83
+ :user_name => nil,
84
+ :password => nil,
85
+ :authentication => nil,
86
+ :enable_starttls => nil,
87
+ :enable_starttls_auto => true,
88
+ :openssl_verify_mode => nil,
89
+ :ssl => nil,
90
+ :tls => nil,
91
+ :open_timeout => 5,
92
+ :read_timeout => 5
93
+ }
94
+
79
95
  def initialize(values)
80
- self.settings = { :address => "localhost",
81
- :port => 25,
82
- :domain => 'localhost.localdomain',
83
- :user_name => nil,
84
- :password => nil,
85
- :authentication => nil,
86
- :enable_starttls_auto => true,
87
- :openssl_verify_mode => nil,
88
- :ssl => nil,
89
- :tls => nil
90
- }.merge!(values)
96
+ self.settings = DEFAULTS.merge(values)
91
97
  end
92
98
 
93
- # Send the message via SMTP.
94
- # The from and to attributes are optional. If not set, they are retrieve from the Message.
95
99
  def deliver!(mail)
96
- smtp_from, smtp_to, message = Mail::CheckDeliveryParams.check(mail)
97
-
98
- smtp = Net::SMTP.new(settings[:address], settings[:port])
99
- if settings[:tls] || settings[:ssl]
100
- if smtp.respond_to?(:enable_tls)
101
- smtp.enable_tls(ssl_context)
102
- end
103
- elsif settings[:enable_starttls_auto]
104
- if smtp.respond_to?(:enable_starttls_auto)
105
- smtp.enable_starttls_auto(ssl_context)
106
- end
100
+ response = start_smtp_session do |smtp|
101
+ Mail::SMTPConnection.new(:connection => smtp, :return_response => true).deliver!(mail)
107
102
  end
108
103
 
109
- response = nil
110
- smtp.start(settings[:domain], settings[:user_name], settings[:password], settings[:authentication]) do |smtp_obj|
111
- response = smtp_obj.sendmail(message, smtp_from, smtp_to)
112
- end
113
-
114
- if settings[:return_response]
115
- response
116
- else
117
- self
118
- end
104
+ settings[:return_response] ? response : self
119
105
  end
120
106
 
121
107
  private
108
+ def start_smtp_session(&block)
109
+ build_smtp_session.start(settings[:domain], settings[:user_name], settings[:password], settings[:authentication], &block)
110
+ end
122
111
 
123
- # Allow SSL context to be configured via settings, for Ruby >= 1.9
124
- # Just returns openssl verify mode for Ruby 1.8.x
125
- def ssl_context
126
- openssl_verify_mode = settings[:openssl_verify_mode]
112
+ def build_smtp_session
113
+ Net::SMTP.new(settings[:address], settings[:port]).tap do |smtp|
114
+ tls = settings[:tls] || settings[:ssl]
115
+ if !tls.nil?
116
+ case tls
117
+ when true
118
+ smtp.enable_tls(ssl_context)
119
+ when false
120
+ smtp.disable_tls
121
+ else
122
+ raise ArgumentError, "Unrecognized :tls value #{settings[:tls].inspect}; expected true, false, or nil"
123
+ end
124
+ elsif settings.include?(:enable_starttls) && !settings[:enable_starttls].nil?
125
+ case settings[:enable_starttls]
126
+ when true
127
+ smtp.enable_starttls(ssl_context)
128
+ when false
129
+ smtp.disable_starttls
130
+ else
131
+ raise ArgumentError, "Unrecognized :enable_starttls value #{settings[:enable_starttls].inspect}; expected true, false, or nil"
132
+ end
133
+ elsif settings.include?(:enable_starttls_auto) && !settings[:enable_starttls_auto].nil?
134
+ case settings[:enable_starttls_auto]
135
+ when true
136
+ smtp.enable_starttls_auto(ssl_context)
137
+ when false
138
+ smtp.disable_starttls
139
+ else
140
+ raise ArgumentError, "Unrecognized :enable_starttls_auto value #{settings[:enable_starttls_auto].inspect}; expected true, false, or nil"
141
+ end
142
+ end
127
143
 
128
- if openssl_verify_mode.kind_of?(String)
129
- openssl_verify_mode = "OpenSSL::SSL::VERIFY_#{openssl_verify_mode.upcase}".constantize
144
+ smtp.open_timeout = settings[:open_timeout] if settings[:open_timeout]
145
+ smtp.read_timeout = settings[:read_timeout] if settings[:read_timeout]
146
+ end
130
147
  end
131
148
 
132
- context = Net::SMTP.default_ssl_context
133
- context.verify_mode = openssl_verify_mode
134
- context.ca_path = settings[:ca_path] if settings[:ca_path]
135
- context.ca_file = settings[:ca_file] if settings[:ca_file]
136
- context
137
- end
149
+ # Allow SSL context to be configured via settings, for Ruby >= 1.9
150
+ # Just returns openssl verify mode for Ruby 1.8.x
151
+ def ssl_context
152
+ openssl_verify_mode = settings[:openssl_verify_mode]
153
+
154
+ if openssl_verify_mode.kind_of?(String)
155
+ openssl_verify_mode = OpenSSL::SSL.const_get("VERIFY_#{openssl_verify_mode.upcase}")
156
+ end
157
+
158
+ context = Net::SMTP.default_ssl_context
159
+ context.verify_mode = openssl_verify_mode if openssl_verify_mode
160
+ context.ca_path = settings[:ca_path] if settings[:ca_path]
161
+ context.ca_file = settings[:ca_file] if settings[:ca_file]
162
+ context
163
+ end
138
164
  end
139
165
  end
@@ -1,4 +1,5 @@
1
- require 'mail/check_delivery_params'
1
+ # frozen_string_literal: true
2
+ require 'mail/smtp_envelope'
2
3
 
3
4
  module Mail
4
5
  # == Sending Email with SMTP
@@ -48,9 +49,8 @@ module Mail
48
49
  # Send the message via SMTP.
49
50
  # The from and to attributes are optional. If not set, they are retrieve from the Message.
50
51
  def deliver!(mail)
51
- smtp_from, smtp_to, message = Mail::CheckDeliveryParams.check(mail)
52
- response = smtp.sendmail(message, smtp_from, smtp_to)
53
-
52
+ envelope = Mail::SmtpEnvelope.new(mail)
53
+ response = smtp.sendmail(envelope.message, envelope.from, envelope.to)
54
54
  settings[:return_response] ? response : self
55
55
  end
56
56
  end
@@ -1,4 +1,5 @@
1
- require 'mail/check_delivery_params'
1
+ # frozen_string_literal: true
2
+ require 'mail/smtp_envelope'
2
3
 
3
4
  module Mail
4
5
  # The TestMailer is a bare bones mailer that does nothing. It is useful
@@ -34,7 +35,9 @@ module Mail
34
35
  end
35
36
 
36
37
  def deliver!(mail)
37
- Mail::CheckDeliveryParams.check(mail)
38
+ # Create the envelope to validate it
39
+ Mail::SmtpEnvelope.new(mail)
40
+
38
41
  Mail::TestMailer.deliveries << mail
39
42
  end
40
43
  end
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module Mail
4
5
 
@@ -10,8 +11,8 @@ module Mail
10
11
  # count: number of emails to retrieve. The default value is 1.
11
12
  # order: order of emails returned. Possible values are :asc or :desc. Default value is :asc.
12
13
  #
13
- def first(options = {}, &block)
14
- options ||= {}
14
+ def first(options = nil, &block)
15
+ options = options ? Hash[options] : {}
15
16
  options[:what] = :first
16
17
  options[:count] ||= 1
17
18
  find(options, &block)
@@ -23,8 +24,8 @@ module Mail
23
24
  # count: number of emails to retrieve. The default value is 1.
24
25
  # order: order of emails returned. Possible values are :asc or :desc. Default value is :asc.
25
26
  #
26
- def last(options = {}, &block)
27
- options ||= {}
27
+ def last(options = nil, &block)
28
+ options = options ? Hash[options] : {}
28
29
  options[:what] = :last
29
30
  options[:count] ||= 1
30
31
  find(options, &block)
@@ -35,8 +36,8 @@ module Mail
35
36
  # Possible options:
36
37
  # order: order of emails returned. Possible values are :asc or :desc. Default value is :asc.
37
38
  #
38
- def all(options = {}, &block)
39
- options ||= {}
39
+ def all(options = nil, &block)
40
+ options = options ? Hash[options] : {}
40
41
  options[:count] = :all
41
42
  find(options, &block)
42
43
  end
@@ -52,8 +53,8 @@ module Mail
52
53
  # delete_after_find: flag for whether to delete each retreived email after find. Default
53
54
  # is true. Call #find if you would like this to default to false.
54
55
  #
55
- def find_and_delete(options = {}, &block)
56
- options ||= {}
56
+ def find_and_delete(options = nil, &block)
57
+ options = options ? Hash[options] : {}
57
58
  options[:delete_after_find] ||= true
58
59
  find(options, &block)
59
60
  end
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module Mail
4
5
  # The IMAP retriever allows to get the last, first or all emails from a IMAP server.
@@ -44,7 +45,8 @@ module Mail
44
45
  :user_name => nil,
45
46
  :password => nil,
46
47
  :authentication => nil,
47
- :enable_ssl => false }.merge!(values)
48
+ :enable_ssl => false,
49
+ :enable_starttls => false }.merge!(values)
48
50
  end
49
51
 
50
52
  attr_accessor :settings
@@ -66,38 +68,46 @@ module Mail
66
68
  # keys: are passed as criteria to the SEARCH command. They can either be a string holding the entire search string,
67
69
  # or a single-dimension array of search keywords and arguments. Refer to [IMAP] section 6.4.4 for a full list
68
70
  # The default is 'ALL'
71
+ # search_charset: charset to pass to IMAP server search. Omitted by default. Example: 'UTF-8' or 'ASCII'.
69
72
  #
70
- def find(options={}, &block)
73
+ def find(options=nil, &block)
71
74
  options = validate_options(options)
72
75
 
73
76
  start do |imap|
74
77
  options[:read_only] ? imap.examine(options[:mailbox]) : imap.select(options[:mailbox])
75
-
76
- message_ids = imap.uid_search(options[:keys])
77
- message_ids.reverse! if options[:what].to_sym == :last
78
- message_ids = message_ids.first(options[:count]) if options[:count].is_a?(Integer)
79
- message_ids.reverse! if (options[:what].to_sym == :last && options[:order].to_sym == :asc) ||
78
+ uids = imap.uid_search(options[:keys], options[:search_charset])
79
+ uids.reverse! if options[:what].to_sym == :last
80
+ uids = uids.first(options[:count]) if options[:count].is_a?(Integer)
81
+ uids.reverse! if (options[:what].to_sym == :last && options[:order].to_sym == :asc) ||
80
82
  (options[:what].to_sym != :last && options[:order].to_sym == :desc)
81
83
 
82
84
  if block_given?
83
- message_ids.each do |message_id|
84
- fetchdata = imap.uid_fetch(message_id, ['RFC822'])[0]
85
+ uids.each do |uid|
86
+ uid = options[:uid].to_i unless options[:uid].nil?
87
+ fetchdata = imap.uid_fetch(uid, ['RFC822', 'FLAGS'])[0]
85
88
  new_message = Mail.new(fetchdata.attr['RFC822'])
86
89
  new_message.mark_for_delete = true if options[:delete_after_find]
87
- if block.arity == 3
88
- yield new_message, imap, message_id
90
+
91
+ if block.arity == 4
92
+ yield new_message, imap, uid, fetchdata.attr['FLAGS']
93
+ elsif block.arity == 3
94
+ yield new_message, imap, uid
89
95
  else
90
96
  yield new_message
91
97
  end
92
- imap.uid_store(message_id, "+FLAGS", [Net::IMAP::DELETED]) if options[:delete_after_find] && new_message.is_marked_for_delete?
98
+
99
+ imap.uid_store(uid, "+FLAGS", [Net::IMAP::DELETED]) if options[:delete_after_find] && new_message.is_marked_for_delete?
100
+ break unless options[:uid].nil?
93
101
  end
94
102
  imap.expunge if options[:delete_after_find]
95
103
  else
96
104
  emails = []
97
- message_ids.each do |message_id|
98
- fetchdata = imap.uid_fetch(message_id, ['RFC822'])[0]
105
+ uids.each do |uid|
106
+ uid = options[:uid].to_i unless options[:uid].nil?
107
+ fetchdata = imap.uid_fetch(uid, ['RFC822'])[0]
99
108
  emails << Mail.new(fetchdata.attr['RFC822'])
100
- imap.uid_store(message_id, "+FLAGS", [Net::IMAP::DELETED]) if options[:delete_after_find]
109
+ imap.uid_store(uid, "+FLAGS", [Net::IMAP::DELETED]) if options[:delete_after_find]
110
+ break unless options[:uid].nil?
101
111
  end
102
112
  imap.expunge if options[:delete_after_find]
103
113
  emails.size == 1 && options[:count] == 1 ? emails.first : emails
@@ -111,8 +121,9 @@ module Mail
111
121
  mailbox = Net::IMAP.encode_utf7(mailbox)
112
122
 
113
123
  start do |imap|
114
- imap.uid_search(['ALL']).each do |message_id|
115
- imap.uid_store(message_id, "+FLAGS", [Net::IMAP::DELETED])
124
+ imap.select(mailbox)
125
+ imap.uid_search(['ALL']).each do |uid|
126
+ imap.uid_store(uid, "+FLAGS", [Net::IMAP::DELETED])
116
127
  end
117
128
  imap.expunge
118
129
  end
@@ -131,12 +142,13 @@ module Mail
131
142
 
132
143
  # Set default options
133
144
  def validate_options(options)
134
- options ||= {}
145
+ options = options ? Hash[options] : {}
135
146
  options[:mailbox] ||= 'INBOX'
136
147
  options[:count] ||= 10
137
148
  options[:order] ||= :asc
138
149
  options[:what] ||= :first
139
150
  options[:keys] ||= 'ALL'
151
+ options[:uid] ||= nil
140
152
  options[:delete_after_find] ||= false
141
153
  options[:mailbox] = Net::IMAP.encode_utf7(options[:mailbox])
142
154
  options[:read_only] ||= false
@@ -148,7 +160,14 @@ module Mail
148
160
  def start(config=Mail::Configuration.instance, &block)
149
161
  raise ArgumentError.new("Mail::Retrievable#imap_start takes a block") unless block_given?
150
162
 
163
+ if settings[:enable_starttls] && settings[:enable_ssl]
164
+ 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."
165
+ end
166
+
151
167
  imap = Net::IMAP.new(settings[:address], settings[:port], settings[:enable_ssl], nil, false)
168
+
169
+ imap.starttls if settings[:enable_starttls]
170
+
152
171
  if settings[:authentication].nil?
153
172
  imap.login(settings[:user_name], settings[:password])
154
173
  else
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module Mail
4
5
  # The Pop3 retriever allows to get the last, first or all emails from a POP3 server.
@@ -40,7 +41,8 @@ module Mail
40
41
  :user_name => nil,
41
42
  :password => nil,
42
43
  :authentication => nil,
43
- :enable_ssl => false }.merge!(values)
44
+ :enable_ssl => false,
45
+ :read_timeout => nil }.merge!(values)
44
46
  end
45
47
 
46
48
  attr_accessor :settings
@@ -55,7 +57,7 @@ module Mail
55
57
  # delete_after_find: flag for whether to delete each retreived email after find. Default
56
58
  # is false. Use #find_and_delete if you would like this to default to true.
57
59
  #
58
- def find(options = {}, &block)
60
+ def find(options = nil, &block)
59
61
  options = validate_options(options)
60
62
 
61
63
  start do |pop3|
@@ -111,7 +113,7 @@ module Mail
111
113
 
112
114
  # Set default options
113
115
  def validate_options(options)
114
- options ||= {}
116
+ options = options ? Hash[options] : {}
115
117
  options[:count] ||= 10
116
118
  options[:order] ||= :asc
117
119
  options[:what] ||= :first
@@ -127,6 +129,7 @@ module Mail
127
129
 
128
130
  pop3 = Net::POP3.new(settings[:address], settings[:port], false)
129
131
  pop3.enable_ssl(OpenSSL::SSL::VERIFY_NONE) if settings[:enable_ssl]
132
+ pop3.read_timeout = settings[:read_timeout] if settings[:read_timeout]
130
133
  pop3.start(settings[:user_name], settings[:password])
131
134
 
132
135
  yield pop3
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module Mail
4
5
 
@@ -16,7 +17,8 @@ module Mail
16
17
  @@emails = []
17
18
  end
18
19
 
19
- def find(options = {}, &block)
20
+ def find(options = nil, &block)
21
+ options = options ? Hash[options] : {}
20
22
  options[:count] ||= :all
21
23
  options[:order] ||= :asc
22
24
  options[:what] ||= :first
@@ -24,7 +26,7 @@ module Mail
24
26
  emails_index.reverse! if options[:what] == :last
25
27
  emails_index = case count = options[:count]
26
28
  when :all then emails_index
27
- when Fixnum then emails_index[0, count]
29
+ when Integer then emails_index[0, count]
28
30
  else
29
31
  raise 'Invalid count option value: ' + count.inspect
30
32
  end
data/lib/mail/network.rb CHANGED
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
1
2
  require 'mail/network/retriever_methods/base'
2
3
 
3
4
  module Mail
4
5
  register_autoload :SMTP, 'mail/network/delivery_methods/smtp'
5
6
  register_autoload :FileDelivery, 'mail/network/delivery_methods/file_delivery'
7
+ register_autoload :LoggerDelivery, 'mail/network/delivery_methods/logger_delivery'
6
8
  register_autoload :Sendmail, 'mail/network/delivery_methods/sendmail'
7
9
  register_autoload :Exim, 'mail/network/delivery_methods/exim'
8
10
  register_autoload :SMTPConnection, 'mail/network/delivery_methods/smtp_connection'
@@ -0,0 +1,15 @@
1
+ module Mail
2
+ # Extends each field parser with utility methods.
3
+ module ParserTools #:nodoc:
4
+ # Slice bytes from ASCII-8BIT data and mark as UTF-8.
5
+ if 'string'.respond_to?(:force_encoding)
6
+ def chars(data, from_bytes, to_bytes)
7
+ data.slice(from_bytes..to_bytes).force_encoding(Encoding::UTF_8)
8
+ end
9
+ else
10
+ def chars(data, from_bytes, to_bytes)
11
+ data.slice(from_bytes..to_bytes)
12
+ end
13
+ end
14
+ end
15
+ end