mail 2.6.1 → 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 (188) hide show
  1. checksums.yaml +5 -5
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +150 -107
  4. data/lib/mail/attachments_list.rb +13 -10
  5. data/lib/mail/body.rb +104 -90
  6. data/lib/mail/check_delivery_params.rb +55 -10
  7. data/lib/mail/configuration.rb +3 -0
  8. data/lib/mail/constants.rb +79 -0
  9. data/lib/mail/elements/address.rb +96 -108
  10. data/lib/mail/elements/address_list.rb +13 -30
  11. data/lib/mail/elements/content_disposition_element.rb +10 -16
  12. data/lib/mail/elements/content_location_element.rb +9 -13
  13. data/lib/mail/elements/content_transfer_encoding_element.rb +7 -11
  14. data/lib/mail/elements/content_type_element.rb +17 -23
  15. data/lib/mail/elements/date_time_element.rb +8 -15
  16. data/lib/mail/elements/envelope_from_element.rb +23 -23
  17. data/lib/mail/elements/message_ids_element.rb +22 -15
  18. data/lib/mail/elements/mime_version_element.rb +8 -15
  19. data/lib/mail/elements/phrase_list.rb +13 -10
  20. data/lib/mail/elements/received_element.rb +28 -19
  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 -92
  31. data/lib/mail/envelope.rb +12 -14
  32. data/lib/mail/field.rb +121 -85
  33. data/lib/mail/field_list.rb +62 -8
  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 +32 -31
  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 +36 -50
  73. data/lib/mail/fields.rb +1 -0
  74. data/lib/mail/header.rb +73 -110
  75. data/lib/mail/indifferent_hash.rb +1 -0
  76. data/lib/mail/mail.rb +6 -11
  77. data/lib/mail/matchers/attachment_matchers.rb +44 -0
  78. data/lib/mail/matchers/has_sent_mail.rb +53 -9
  79. data/lib/mail/message.rb +132 -136
  80. data/lib/mail/multibyte/chars.rb +24 -180
  81. data/lib/mail/multibyte/unicode.rb +31 -26
  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 +9 -11
  85. data/lib/mail/network/delivery_methods/file_delivery.rb +14 -16
  86. data/lib/mail/network/delivery_methods/logger_delivery.rb +34 -0
  87. data/lib/mail/network/delivery_methods/sendmail.rb +68 -24
  88. data/lib/mail/network/delivery_methods/smtp.rb +77 -54
  89. data/lib/mail/network/delivery_methods/smtp_connection.rb +5 -9
  90. data/lib/mail/network/delivery_methods/test_mailer.rb +9 -9
  91. data/lib/mail/network/retriever_methods/base.rb +9 -8
  92. data/lib/mail/network/retriever_methods/imap.rb +21 -7
  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 +33226 -116
  98. data/lib/mail/parsers/address_lists_parser.rl +179 -0
  99. data/lib/mail/parsers/content_disposition_parser.rb +883 -49
  100. data/lib/mail/parsers/content_disposition_parser.rl +89 -0
  101. data/lib/mail/parsers/content_location_parser.rb +810 -23
  102. data/lib/mail/parsers/content_location_parser.rl +78 -0
  103. data/lib/mail/parsers/content_transfer_encoding_parser.rb +510 -21
  104. data/lib/mail/parsers/content_transfer_encoding_parser.rl +71 -0
  105. data/lib/mail/parsers/content_type_parser.rb +1031 -47
  106. data/lib/mail/parsers/content_type_parser.rl +90 -0
  107. data/lib/mail/parsers/date_time_parser.rb +879 -24
  108. data/lib/mail/parsers/date_time_parser.rl +69 -0
  109. data/lib/mail/parsers/envelope_from_parser.rb +3670 -40
  110. data/lib/mail/parsers/envelope_from_parser.rl +89 -0
  111. data/lib/mail/parsers/message_ids_parser.rb +5147 -25
  112. data/lib/mail/parsers/message_ids_parser.rl +93 -0
  113. data/lib/mail/parsers/mime_version_parser.rb +498 -26
  114. data/lib/mail/parsers/mime_version_parser.rl +68 -0
  115. data/lib/mail/parsers/phrase_lists_parser.rb +872 -21
  116. data/lib/mail/parsers/phrase_lists_parser.rl +90 -0
  117. data/lib/mail/parsers/received_parser.rb +8777 -42
  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/{ragel/date_time.rl → rfc5322_date_time.rl} +8 -1
  128. data/lib/mail/parsers/rfc5322_lexical_tokens.rl +60 -0
  129. data/lib/mail/parsers.rb +12 -25
  130. data/lib/mail/part.rb +11 -12
  131. data/lib/mail/parts_list.rb +88 -14
  132. data/lib/mail/smtp_envelope.rb +57 -0
  133. data/lib/mail/utilities.rb +377 -40
  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 +138 -94
  139. data/CHANGELOG.rdoc +0 -752
  140. data/CONTRIBUTING.md +0 -60
  141. data/Dependencies.txt +0 -2
  142. data/Gemfile +0 -15
  143. data/Rakefile +0 -29
  144. data/TODO.rdoc +0 -9
  145. data/VERSION +0 -4
  146. data/lib/mail/core_extensions/nil.rb +0 -19
  147. data/lib/mail/core_extensions/object.rb +0 -13
  148. data/lib/mail/core_extensions/smtp.rb +0 -24
  149. data/lib/mail/core_extensions/string/access.rb +0 -145
  150. data/lib/mail/core_extensions/string/multibyte.rb +0 -78
  151. data/lib/mail/core_extensions/string.rb +0 -43
  152. data/lib/mail/fields/common/address_container.rb +0 -16
  153. data/lib/mail/fields/common/common_address.rb +0 -135
  154. data/lib/mail/fields/common/common_date.rb +0 -35
  155. data/lib/mail/fields/common/common_field.rb +0 -57
  156. data/lib/mail/fields/common/common_message_id.rb +0 -48
  157. data/lib/mail/multibyte/exceptions.rb +0 -8
  158. data/lib/mail/parsers/ragel/common.rl +0 -184
  159. data/lib/mail/parsers/ragel/parser_info.rb +0 -61
  160. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +0 -14864
  161. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +0 -37
  162. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +0 -751
  163. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +0 -37
  164. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +0 -614
  165. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +0 -37
  166. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +0 -447
  167. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +0 -37
  168. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +0 -825
  169. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +0 -37
  170. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +0 -817
  171. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +0 -37
  172. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +0 -2129
  173. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +0 -37
  174. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +0 -1570
  175. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +0 -37
  176. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +0 -440
  177. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +0 -37
  178. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +0 -564
  179. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +0 -37
  180. data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +0 -51
  181. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +0 -5144
  182. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +0 -37
  183. data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +0 -37
  184. data/lib/mail/parsers/ragel/ruby.rb +0 -39
  185. data/lib/mail/parsers/ragel.rb +0 -17
  186. data/lib/mail/patterns.rb +0 -37
  187. data/lib/mail/version_specific/ruby_1_8.rb +0 -119
  188. data/lib/mail/version_specific/ruby_1_9.rb +0 -159
@@ -1,5 +1,7 @@
1
1
  # encoding: utf-8
2
- require 'mail/fields/common/common_field'
2
+ # frozen_string_literal: true
3
+ require 'mail/fields/common_field'
4
+ require 'mail/utilities'
3
5
 
4
6
  module Mail
5
7
  # Provides access to an unstructured header field
@@ -14,63 +16,43 @@ module Mail
14
16
  # field bodies are simply to be treated as a single line of characters
15
17
  # with no further processing (except for header "folding" and
16
18
  # "unfolding" as described in section 2.2.3).
17
- class UnstructuredField
18
-
19
- include Mail::CommonField
20
- include Mail::Utilities
21
-
22
- attr_accessor :charset
23
- attr_reader :errors
24
-
19
+ class UnstructuredField < CommonField #:nodoc:
25
20
  def initialize(name, value, charset = nil)
26
- @errors = []
27
-
28
21
  if value.is_a?(Array)
29
22
  # Probably has arrived here from a failed parse of an AddressList Field
30
23
  value = value.join(', ')
31
- else
32
- # Ensure we are dealing with a string
33
- value = value.to_s
24
+
25
+ # Mark UTF-8 strings parsed from ASCII-8BIT
26
+ elsif value.respond_to?(:force_encoding) && value.encoding == Encoding::ASCII_8BIT
27
+ utf8 = value.dup.force_encoding(Encoding::UTF_8)
28
+ value = utf8 if utf8.valid_encoding?
34
29
  end
35
30
 
36
- if charset
37
- self.charset = charset
38
- else
31
+ charset ||=
39
32
  if value.respond_to?(:encoding)
40
- self.charset = value.encoding
41
- else
42
- self.charset = $KCODE
33
+ value.encoding
43
34
  end
44
- end
45
- self.name = name
46
- self.value = value
47
- self
48
- end
49
35
 
50
- def encoded
51
- do_encode
36
+ super name, value.to_s, charset
52
37
  end
53
38
 
54
- def decoded
55
- do_decode
56
- end
57
-
58
- def default
59
- decoded
60
- end
61
-
62
- def parse # An unstructured field does not parse
39
+ # An unstructured field does not parse
40
+ def parse
63
41
  self
64
42
  end
65
43
 
66
44
  private
67
45
 
68
46
  def do_encode
69
- value.nil? ? '' : "#{wrapped_value}\r\n"
47
+ if value && !value.empty?
48
+ "#{wrapped_value}\r\n"
49
+ else
50
+ ''
51
+ end
70
52
  end
71
53
 
72
54
  def do_decode
73
- value.blank? ? nil : Encodings.decode_encode(value, :decode)
55
+ Utilities.blank?(value) ? nil : Encodings.decode_encode(value, :decode)
74
56
  end
75
57
 
76
58
  # 2.2.3. Long Header Fields
@@ -120,16 +102,16 @@ module Mail
120
102
  def fold(prepend = 0) # :nodoc:
121
103
  encoding = normalized_encoding
122
104
  decoded_string = decoded.to_s
123
- should_encode = decoded_string.not_ascii_only?
105
+ should_encode = !decoded_string.ascii_only?
124
106
  if should_encode
125
107
  first = true
126
108
  words = decoded_string.split(/[ \t]/).map do |word|
127
109
  if first
128
110
  first = !first
129
111
  else
130
- word = " " << word
112
+ word = " #{word}"
131
113
  end
132
- if word.not_ascii_only?
114
+ if !word.ascii_only?
133
115
  word
134
116
  else
135
117
  word.scan(/.{7}|.+$/)
@@ -143,11 +125,18 @@ module Mail
143
125
  while !words.empty?
144
126
  limit = 78 - prepend
145
127
  limit = limit - 7 - encoding.length if should_encode
146
- line = ""
128
+ line = String.new
147
129
  first_word = true
148
130
  while !words.empty?
149
131
  break unless word = words.first.dup
150
- word.encode!(charset) if charset && word.respond_to?(:encode!)
132
+
133
+ # Convert on 1.9+ only since we aren't sure of the current
134
+ # charset encoding on 1.8. We'd need to track internal/external
135
+ # charset on each field.
136
+ if charset && word.respond_to?(:encoding)
137
+ word = Encodings.transcode_charset(word, word.encoding, charset)
138
+ end
139
+
151
140
  word = encode(word) if should_encode
152
141
  word = encode_crlf(word)
153
142
  # Skip to next line if we're going to go past the limit
@@ -178,7 +167,7 @@ module Mail
178
167
  end
179
168
 
180
169
  def encode(value)
181
- value = [value].pack("M").gsub("=\n", '')
170
+ value = [value].pack(Constants::CAPITAL_M).gsub(Constants::EQUAL_LF, Constants::EMPTY)
182
171
  value.gsub!(/"/, '=22')
183
172
  value.gsub!(/\(/, '=28')
184
173
  value.gsub!(/\)/, '=29')
@@ -189,16 +178,13 @@ module Mail
189
178
  end
190
179
 
191
180
  def encode_crlf(value)
192
- value.gsub!("\r", '=0D')
193
- value.gsub!("\n", '=0A')
181
+ value.gsub!(Constants::CR, Constants::CR_ENCODED)
182
+ value.gsub!(Constants::LF, Constants::LF_ENCODED)
194
183
  value
195
184
  end
196
185
 
197
186
  def normalized_encoding
198
- encoding = charset.to_s.upcase.gsub('_', '-')
199
- encoding = 'UTF-8' if encoding == 'UTF8' # Ruby 1.8.x and $KCODE == 'u'
200
- encoding
187
+ charset.to_s.upcase.gsub('_', '-')
201
188
  end
202
-
203
189
  end
204
190
  end
data/lib/mail/fields.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Mail
2
3
  register_autoload :UnstructuredField, 'mail/fields/unstructured_field'
3
4
  register_autoload :StructuredField, 'mail/fields/structured_field'
data/lib/mail/header.rb CHANGED
@@ -1,12 +1,15 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
3
+ require 'mail/constants'
4
+ require 'mail/utilities'
5
+
2
6
  module Mail
3
-
4
7
  # Provides access to a header object.
5
- #
8
+ #
6
9
  # ===Per RFC2822
7
- #
10
+ #
8
11
  # 2.2. Header Fields
9
- #
12
+ #
10
13
  # Header fields are lines composed of a field name, followed by a colon
11
14
  # (":"), followed by a field body, and terminated by CRLF. A field
12
15
  # name MUST be composed of printable US-ASCII characters (i.e.,
@@ -17,14 +20,12 @@ module Mail
17
20
  # 2.2.3. All field bodies MUST conform to the syntax described in
18
21
  # sections 3 and 4 of this standard.
19
22
  class Header
20
- include Patterns
21
- include Utilities
22
23
  include Enumerable
23
-
24
+
24
25
  @@maximum_amount = 1000
25
26
 
26
27
  # Large amount of headers in Email might create extra high CPU load
27
- # Use this parameter to limit number of headers that will be parsed by
28
+ # Use this parameter to limit number of headers that will be parsed by
28
29
  # mail library.
29
30
  # Default: 1000
30
31
  def self.maximum_amount
@@ -35,12 +36,14 @@ module Mail
35
36
  @@maximum_amount = value
36
37
  end
37
38
 
39
+ attr_reader :raw_source, :charset
40
+
38
41
  # Creates a new header object.
39
- #
42
+ #
40
43
  # Accepts raw text or nothing. If given raw text will attempt to parse
41
44
  # it and split it into the various fields, instantiating each field as
42
45
  # it goes.
43
- #
46
+ #
44
47
  # If it finds a field that should be a structured field (such as content
45
48
  # type), but it fails to parse it, it will simply make it an unstructured
46
49
  # field and leave it alone. This will mean that the data is preserved but
@@ -49,29 +52,24 @@ module Mail
49
52
  # me the example so we can fix it.
50
53
  def initialize(header_text = nil, charset = nil)
51
54
  @charset = charset
52
- self.raw_source = header_text.to_crlf.lstrip
55
+ @raw_source = ::Mail::Utilities.to_crlf(header_text).lstrip
53
56
  split_header if header_text
54
57
  end
55
58
 
56
59
  def initialize_copy(original)
57
60
  super
58
61
  @fields = @fields.dup
62
+ @fields.map!(&:dup)
59
63
  end
60
-
61
- # The preserved raw source of the header as you passed it in, untouched
62
- # for your Regexing glory.
63
- def raw_source
64
- @raw_source
65
- end
66
-
64
+
67
65
  # Returns an array of all the fields in the header in order that they
68
66
  # were read in.
69
67
  def fields
70
68
  @fields ||= FieldList.new
71
69
  end
72
-
70
+
73
71
  # 3.6. Field definitions
74
- #
72
+ #
75
73
  # It is important to note that the header fields are not guaranteed to
76
74
  # be in a particular order. They may appear in any order, and they
77
75
  # have been known to be reordered occasionally when transported over
@@ -81,35 +79,35 @@ module Mail
81
79
  # header fields MUST NOT be reordered, and SHOULD be kept in blocks
82
80
  # prepended to the message. See sections 3.6.6 and 3.6.7 for more
83
81
  # information.
84
- #
82
+ #
85
83
  # Populates the fields container with Field objects in the order it
86
84
  # receives them in.
87
85
  #
88
86
  # Acceps an array of field string values, for example:
89
- #
87
+ #
90
88
  # h = Header.new
91
89
  # h.fields = ['From: mikel@me.com', 'To: bob@you.com']
92
90
  def fields=(unfolded_fields)
93
91
  @fields = Mail::FieldList.new
94
- warn "Warning: more than #{self.class.maximum_amount} header fields only using the first #{self.class.maximum_amount}" if unfolded_fields.length > self.class.maximum_amount
95
- unfolded_fields[0..(self.class.maximum_amount-1)].each do |field|
96
92
 
97
- field = Field.new(field, nil, charset)
98
- if limited_field?(field.name) && (selected = select_field_for(field.name)) && selected.any?
99
- selected.first.update(field.name, field.value)
100
- else
101
- @fields << field
102
- end
93
+ if unfolded_fields.size > self.class.maximum_amount
94
+ Kernel.warn "WARNING: More than #{self.class.maximum_amount} header fields; only using the first #{self.class.maximum_amount} and ignoring the rest"
95
+ unfolded_fields = unfolded_fields.slice(0...self.class.maximum_amount)
103
96
  end
104
97
 
98
+ unfolded_fields.each do |field|
99
+ if field = Field.parse(field, charset)
100
+ @fields.add_field field
101
+ end
102
+ end
105
103
  end
106
-
104
+
107
105
  def errors
108
106
  @fields.map(&:errors).flatten(1)
109
107
  end
110
-
108
+
111
109
  # 3.6. Field definitions
112
- #
110
+ #
113
111
  # The following table indicates limits on the number of times each
114
112
  # field may occur in a message header as well as any special
115
113
  # limitations on the use of those fields. An asterisk next to a value
@@ -119,34 +117,25 @@ module Mail
119
117
  # <snip table from 3.6>
120
118
  #
121
119
  # As per RFC, many fields can appear more than once, we will return a string
122
- # of the value if there is only one header, or if there is more than one
120
+ # of the value if there is only one header, or if there is more than one
123
121
  # matching header, will return an array of values in order that they appear
124
122
  # in the header ordered from top to bottom.
125
- #
123
+ #
126
124
  # Example:
127
- #
125
+ #
128
126
  # h = Header.new
129
127
  # h.fields = ['To: mikel@me.com', 'X-Mail-SPAM: 15', 'X-Mail-SPAM: 20']
130
128
  # h['To'] #=> 'mikel@me.com'
131
129
  # h['X-Mail-SPAM'] #=> ['15', '20']
132
130
  def [](name)
133
- name = dasherize(name).downcase
134
- selected = select_field_for(name)
135
- case
136
- when selected.length > 1
137
- selected.map { |f| f }
138
- when !selected.blank?
139
- selected.first
140
- else
141
- nil
142
- end
131
+ fields.get_field(Utilities.dasherize(name))
143
132
  end
144
-
133
+
145
134
  # Sets the FIRST matching field in the header to passed value, or deletes
146
135
  # the FIRST field matched from the header if passed nil
147
- #
136
+ #
148
137
  # Example:
149
- #
138
+ #
150
139
  # h = Header.new
151
140
  # h.fields = ['To: mikel@me.com', 'X-Mail-SPAM: 15', 'X-Mail-SPAM: 20']
152
141
  # h['To'] = 'bob@you.com'
@@ -156,54 +145,42 @@ module Mail
156
145
  # h['X-Mail-SPAM'] = nil
157
146
  # h['X-Mail-SPAM'] # => nil
158
147
  def []=(name, value)
159
- name = dasherize(name)
160
- if name.include?(':')
148
+ name = name.to_s
149
+ if name.include?(Constants::COLON)
161
150
  raise ArgumentError, "Header names may not contain a colon: #{name.inspect}"
162
151
  end
163
- fn = name.downcase
164
- selected = select_field_for(fn)
165
-
166
- case
167
- # User wants to delete the field
168
- when !selected.blank? && value == nil
169
- fields.delete_if { |f| selected.include?(f) }
170
-
171
- # User wants to change the field
172
- when !selected.blank? && limited_field?(fn)
173
- selected.first.update(fn, value)
174
-
175
- # User wants to create the field
152
+
153
+ name = Utilities.dasherize(name)
154
+
155
+ # Assign nil to delete the field
156
+ if value.nil?
157
+ fields.delete_field name
176
158
  else
177
- # Need to insert in correct order for trace fields
178
- self.fields << Field.new(name.to_s, value, charset)
179
- end
180
- if dasherize(fn) == "content-type"
159
+ fields.add_field Field.new(name.to_s, value, charset)
160
+
181
161
  # Update charset if specified in Content-Type
182
- params = self[:content_type].parameters rescue nil
183
- @charset = params[:charset] if params && params[:charset]
162
+ if name == 'content-type'
163
+ params = self[:content_type].parameters rescue nil
164
+ @charset = params[:charset] if params && params[:charset]
165
+ end
184
166
  end
185
167
  end
186
-
187
- def charset
188
- @charset
189
- end
190
-
168
+
191
169
  def charset=(val)
192
170
  params = self[:content_type].parameters rescue nil
193
171
  if params
194
- params[:charset] = val
172
+ if val
173
+ params[:charset] = val
174
+ else
175
+ params.delete(:charset)
176
+ end
195
177
  end
196
178
  @charset = val
197
179
  end
198
-
199
- LIMITED_FIELDS = %w[ date from sender reply-to to cc bcc
200
- message-id in-reply-to references subject
201
- return-path content-type mime-version
202
- content-transfer-encoding content-description
203
- content-id content-disposition content-location]
180
+
204
181
 
205
182
  def encoded
206
- buffer = ''
183
+ buffer = String.new
207
184
  buffer.force_encoding('us-ascii') if buffer.respond_to?(:force_encoding)
208
185
  fields.each do |field|
209
186
  buffer << field.encoded
@@ -214,61 +191,47 @@ module Mail
214
191
  def to_s
215
192
  encoded
216
193
  end
217
-
194
+
218
195
  def decoded
219
- raise NoMethodError, 'Can not decode an entire header as there could be character set conflicts, try calling #decoded on the various fields.'
196
+ raise NoMethodError, 'Can not decode an entire header as there could be character set conflicts. Try calling #decoded on the various fields.'
220
197
  end
221
198
 
222
199
  def field_summary
223
- fields.map { |f| "<#{f.name}: #{f.value}>" }.join(", ")
200
+ fields.summary
224
201
  end
225
202
 
226
203
  # Returns true if the header has a Message-ID defined (empty or not)
227
204
  def has_message_id?
228
- !fields.select { |f| f.responsible_for?('Message-ID') }.empty?
205
+ fields.has_field? 'Message-ID'
229
206
  end
230
207
 
231
208
  # Returns true if the header has a Content-ID defined (empty or not)
232
209
  def has_content_id?
233
- !fields.select { |f| f.responsible_for?('Content-ID') }.empty?
210
+ fields.has_field? 'Content-ID'
234
211
  end
235
212
 
236
213
  # Returns true if the header has a Date defined (empty or not)
237
214
  def has_date?
238
- !fields.select { |f| f.responsible_for?('Date') }.empty?
215
+ fields.has_field? 'Date'
239
216
  end
240
217
 
241
218
  # Returns true if the header has a MIME version defined (empty or not)
242
219
  def has_mime_version?
243
- !fields.select { |f| f.responsible_for?('Mime-Version') }.empty?
220
+ fields.has_field? 'Mime-Version'
244
221
  end
245
222
 
246
223
  private
247
-
248
- def raw_source=(val)
249
- @raw_source = val
250
- end
251
-
224
+
252
225
  # Splits an unfolded and line break cleaned header into individual field
253
226
  # strings.
254
227
  def split_header
255
- self.fields = raw_source.split(HEADER_SPLIT)
256
- end
257
-
258
- def select_field_for(name)
259
- fields.select { |f| f.responsible_for?(name) }
260
- end
261
-
262
- def limited_field?(name)
263
- LIMITED_FIELDS.include?(name.to_s.downcase)
228
+ self.fields = @raw_source.split(Constants::HEADER_SPLIT)
264
229
  end
265
230
 
266
- # Enumerable support; yield each field in order to the block if there is one,
267
- # or return an Enumerator for them if there isn't.
268
- def each( &block )
269
- return self.fields.each( &block ) if block
270
- self.fields.each
271
- end
272
231
 
232
+ # Enumerable support. Yield each field in order.
233
+ def each(&block)
234
+ fields.each(&block)
235
+ end
273
236
  end
274
237
  end
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  # This is an almost cut and paste from ActiveSupport v3.0.6, copied in here so that Mail
4
5
  # itself does not depend on ActiveSupport to avoid versioning conflicts
data/lib/mail/mail.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  module Mail
3
4
 
4
5
  # Allows you to create a new Mail::Message object.
@@ -89,19 +90,11 @@ module Mail
89
90
  # Each mail object inherits the default set in Mail.delivery_method, however, on
90
91
  # a per email basis, you can override the method:
91
92
  #
92
- # mail.delivery_method :sendmail
93
+ # mail.delivery_method :smtp
93
94
  #
94
95
  # Or you can override the method and pass in settings:
95
96
  #
96
- # mail.delivery_method :sendmail, { :address => 'some.host' }
97
- #
98
- # You can also just modify the settings:
99
- #
100
- # mail.delivery_settings = { :address => 'some.host' }
101
- #
102
- # The passed in hash is just merged against the defaults with +merge!+ and the result
103
- # assigned the mail object. So the above example will change only the :address value
104
- # of the global smtp_settings to be 'some.host', keeping all other values
97
+ # mail.delivery_method :smtp, :address => 'some.host'
105
98
  def self.defaults(&block)
106
99
  Configuration.instance.instance_eval(&block)
107
100
  end
@@ -245,9 +238,11 @@ module Mail
245
238
 
246
239
  protected
247
240
 
241
+ RANDOM_TAG='%x%x_%x%x%d%x'
242
+
248
243
  def self.random_tag
249
244
  t = Time.now
250
- sprintf('%x%x_%x%x%d%x',
245
+ sprintf(RANDOM_TAG,
251
246
  t.to_i, t.tv_usec,
252
247
  $$, Thread.current.object_id.abs, self.uniq, rand(255))
253
248
  end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+ module Mail
3
+ module Matchers
4
+ def any_attachment
5
+ AnyAttachmentMatcher.new
6
+ end
7
+
8
+ def an_attachment_with_filename(filename)
9
+ AttachmentFilenameMatcher.new(filename)
10
+ end
11
+
12
+ def an_attachment_with_mime_type(filename)
13
+ AttachmentMimeTypeMatcher.new(filename)
14
+ end
15
+
16
+ class AnyAttachmentMatcher
17
+ def ===(other)
18
+ other.attachment?
19
+ end
20
+ end
21
+
22
+ class AttachmentFilenameMatcher
23
+ attr_reader :filename
24
+ def initialize(filename)
25
+ @filename = filename
26
+ end
27
+
28
+ def ===(other)
29
+ other.attachment? && other.filename == filename
30
+ end
31
+ end
32
+
33
+ class AttachmentMimeTypeMatcher
34
+ attr_reader :mime_type
35
+ def initialize(mime_type)
36
+ @mime_type = mime_type
37
+ end
38
+
39
+ def ===(other)
40
+ other.attachment? && other.mime_type == mime_type
41
+ end
42
+ end
43
+ end
44
+ end