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
@@ -1,55 +1,34 @@
1
1
  # encoding: utf-8
2
- #
3
- # = To Field
4
- #
5
- # The To field inherits to StructuredField and handles the To: header
6
- # field in the email.
7
- #
8
- # Sending to to a mail message will instantiate a Mail::Field object that
9
- # has a ToField as its field type. This includes all Mail::CommonAddress
10
- # module instance metods.
11
- #
12
- # Only one To field can appear in a header, though it can have multiple
13
- # addresses and groups of addresses.
14
- #
15
- # == Examples:
16
- #
17
- # mail = Mail.new
18
- # mail.to = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
19
- # mail.to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
20
- # mail[:to] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ToField:0x180e1c4
21
- # mail['to'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ToField:0x180e1c4
22
- # mail['To'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ToField:0x180e1c4
23
- #
24
- # mail[:to].encoded #=> 'To: Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net\r\n'
25
- # mail[:to].decoded #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
26
- # mail[:to].addresses #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
27
- # mail[:to].formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
28
- #
29
- require 'mail/fields/common/common_address'
2
+ # frozen_string_literal: true
3
+ require 'mail/fields/common_address_field'
30
4
 
31
5
  module Mail
32
- class ToField < StructuredField
33
-
34
- include Mail::CommonAddress
35
-
36
- FIELD_NAME = 'to'
37
- CAPITALIZED_FIELD = 'To'
38
-
39
- def initialize(value = nil, charset = 'utf-8')
40
- self.charset = charset
41
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
42
- self.parse
43
- self
44
- end
45
-
46
- def encoded
47
- do_encode(CAPITALIZED_FIELD)
48
- end
49
-
50
- def decoded
51
- do_decode
52
- end
53
-
6
+ # = To Field
7
+ #
8
+ # The To field inherits to StructuredField and handles the To: header
9
+ # field in the email.
10
+ #
11
+ # Sending to to a mail message will instantiate a Mail::Field object that
12
+ # has a ToField as its field type. This includes all Mail::CommonAddress
13
+ # module instance metods.
14
+ #
15
+ # Only one To field can appear in a header, though it can have multiple
16
+ # addresses and groups of addresses.
17
+ #
18
+ # == Examples:
19
+ #
20
+ # mail = Mail.new
21
+ # mail.to = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
22
+ # mail.to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
23
+ # mail[:to] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ToField:0x180e1c4
24
+ # mail['to'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ToField:0x180e1c4
25
+ # mail['To'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ToField:0x180e1c4
26
+ #
27
+ # mail[:to].encoded #=> 'To: Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net\r\n'
28
+ # mail[:to].decoded #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
29
+ # mail[:to].addresses #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
30
+ # mail[:to].formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
31
+ class ToField < CommonAddressField #:nodoc:
32
+ NAME = 'To'
54
33
  end
55
34
  end
@@ -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
-
50
- def encoded
51
- do_encode
52
- end
53
35
 
54
- def decoded
55
- do_decode
36
+ super name, value.to_s, charset
56
37
  end
57
38
 
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,10 +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
129
+ first_word = true
147
130
  while !words.empty?
148
131
  break unless word = words.first.dup
149
- 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
+
150
140
  word = encode(word) if should_encode
151
141
  word = encode_crlf(word)
152
142
  # Skip to next line if we're going to go past the limit
@@ -158,7 +148,12 @@ module Mail
158
148
  # Remove the word from the queue ...
159
149
  words.shift
160
150
  # Add word separator
161
- line << " " unless (line.empty? || should_encode)
151
+ if first_word
152
+ first_word = false
153
+ else
154
+ line << " " if !should_encode
155
+ end
156
+
162
157
  # ... add it in encoded form to the current line
163
158
  line << word
164
159
  end
@@ -172,7 +167,7 @@ module Mail
172
167
  end
173
168
 
174
169
  def encode(value)
175
- value = [value].pack("M").gsub("=\n", '')
170
+ value = [value].pack(Constants::CAPITAL_M).gsub(Constants::EQUAL_LF, Constants::EMPTY)
176
171
  value.gsub!(/"/, '=22')
177
172
  value.gsub!(/\(/, '=28')
178
173
  value.gsub!(/\)/, '=29')
@@ -183,16 +178,13 @@ module Mail
183
178
  end
184
179
 
185
180
  def encode_crlf(value)
186
- value.gsub!("\r", '=0D')
187
- value.gsub!("\n", '=0A')
181
+ value.gsub!(Constants::CR, Constants::CR_ENCODED)
182
+ value.gsub!(Constants::LF, Constants::LF_ENCODED)
188
183
  value
189
184
  end
190
185
 
191
186
  def normalized_encoding
192
- encoding = charset.to_s.upcase.gsub('_', '-')
193
- encoding = 'UTF-8' if encoding == 'UTF8' # Ruby 1.8.x and $KCODE == 'u'
194
- encoding
187
+ charset.to_s.upcase.gsub('_', '-')
195
188
  end
196
-
197
189
  end
198
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
@@ -48,26 +51,25 @@ module Mail
48
51
  # these cases, please make a patch and send it in, or at the least, send
49
52
  # me the example so we can fix it.
50
53
  def initialize(header_text = nil, charset = nil)
51
- @errors = []
52
54
  @charset = charset
53
- self.raw_source = header_text.to_crlf.lstrip
55
+ @raw_source = ::Mail::Utilities.to_crlf(header_text).lstrip
54
56
  split_header if header_text
55
57
  end
56
-
57
- # The preserved raw source of the header as you passed it in, untouched
58
- # for your Regexing glory.
59
- def raw_source
60
- @raw_source
58
+
59
+ def initialize_copy(original)
60
+ super
61
+ @fields = @fields.dup
62
+ @fields.map!(&:dup)
61
63
  end
62
-
64
+
63
65
  # Returns an array of all the fields in the header in order that they
64
66
  # were read in.
65
67
  def fields
66
68
  @fields ||= FieldList.new
67
69
  end
68
-
70
+
69
71
  # 3.6. Field definitions
70
- #
72
+ #
71
73
  # It is important to note that the header fields are not guaranteed to
72
74
  # be in a particular order. They may appear in any order, and they
73
75
  # have been known to be reordered occasionally when transported over
@@ -77,36 +79,35 @@ module Mail
77
79
  # header fields MUST NOT be reordered, and SHOULD be kept in blocks
78
80
  # prepended to the message. See sections 3.6.6 and 3.6.7 for more
79
81
  # information.
80
- #
82
+ #
81
83
  # Populates the fields container with Field objects in the order it
82
84
  # receives them in.
83
85
  #
84
86
  # Acceps an array of field string values, for example:
85
- #
87
+ #
86
88
  # h = Header.new
87
89
  # h.fields = ['From: mikel@me.com', 'To: bob@you.com']
88
90
  def fields=(unfolded_fields)
89
91
  @fields = Mail::FieldList.new
90
- 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
91
- unfolded_fields[0..(self.class.maximum_amount-1)].each do |field|
92
92
 
93
- field = Field.new(field, nil, charset)
94
- field.errors.each { |error| self.errors << error }
95
- if limited_field?(field.name) && (selected = select_field_for(field.name)) && selected.any?
96
- selected.first.update(field.name, field.value)
97
- else
98
- @fields << field
99
- 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)
100
96
  end
101
97
 
98
+ unfolded_fields.each do |field|
99
+ if field = Field.parse(field, charset)
100
+ @fields.add_field field
101
+ end
102
+ end
102
103
  end
103
-
104
+
104
105
  def errors
105
- @errors
106
+ @fields.map(&:errors).flatten(1)
106
107
  end
107
-
108
+
108
109
  # 3.6. Field definitions
109
- #
110
+ #
110
111
  # The following table indicates limits on the number of times each
111
112
  # field may occur in a message header as well as any special
112
113
  # limitations on the use of those fields. An asterisk next to a value
@@ -116,34 +117,25 @@ module Mail
116
117
  # <snip table from 3.6>
117
118
  #
118
119
  # As per RFC, many fields can appear more than once, we will return a string
119
- # 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
120
121
  # matching header, will return an array of values in order that they appear
121
122
  # in the header ordered from top to bottom.
122
- #
123
+ #
123
124
  # Example:
124
- #
125
+ #
125
126
  # h = Header.new
126
127
  # h.fields = ['To: mikel@me.com', 'X-Mail-SPAM: 15', 'X-Mail-SPAM: 20']
127
128
  # h['To'] #=> 'mikel@me.com'
128
129
  # h['X-Mail-SPAM'] #=> ['15', '20']
129
130
  def [](name)
130
- name = dasherize(name).downcase
131
- selected = select_field_for(name)
132
- case
133
- when selected.length > 1
134
- selected.map { |f| f }
135
- when !selected.blank?
136
- selected.first
137
- else
138
- nil
139
- end
131
+ fields.get_field(Utilities.dasherize(name))
140
132
  end
141
-
133
+
142
134
  # Sets the FIRST matching field in the header to passed value, or deletes
143
135
  # the FIRST field matched from the header if passed nil
144
- #
136
+ #
145
137
  # Example:
146
- #
138
+ #
147
139
  # h = Header.new
148
140
  # h.fields = ['To: mikel@me.com', 'X-Mail-SPAM: 15', 'X-Mail-SPAM: 20']
149
141
  # h['To'] = 'bob@you.com'
@@ -153,54 +145,42 @@ module Mail
153
145
  # h['X-Mail-SPAM'] = nil
154
146
  # h['X-Mail-SPAM'] # => nil
155
147
  def []=(name, value)
156
- name = dasherize(name)
157
- if name.include?(':')
148
+ name = name.to_s
149
+ if name.include?(Constants::COLON)
158
150
  raise ArgumentError, "Header names may not contain a colon: #{name.inspect}"
159
151
  end
160
- fn = name.downcase
161
- selected = select_field_for(fn)
162
-
163
- case
164
- # User wants to delete the field
165
- when !selected.blank? && value == nil
166
- fields.delete_if { |f| selected.include?(f) }
167
-
168
- # User wants to change the field
169
- when !selected.blank? && limited_field?(fn)
170
- selected.first.update(fn, value)
171
-
172
- # 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
173
158
  else
174
- # Need to insert in correct order for trace fields
175
- self.fields << Field.new(name.to_s, value, charset)
176
- end
177
- if dasherize(fn) == "content-type"
159
+ fields.add_field Field.new(name.to_s, value, charset)
160
+
178
161
  # Update charset if specified in Content-Type
179
- params = self[:content_type].parameters rescue nil
180
- @charset = 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
181
166
  end
182
167
  end
183
-
184
- def charset
185
- @charset
186
- end
187
-
168
+
188
169
  def charset=(val)
189
170
  params = self[:content_type].parameters rescue nil
190
171
  if params
191
- params[:charset] = val
172
+ if val
173
+ params[:charset] = val
174
+ else
175
+ params.delete(:charset)
176
+ end
192
177
  end
193
178
  @charset = val
194
179
  end
195
-
196
- LIMITED_FIELDS = %w[ date from sender reply-to to cc bcc
197
- message-id in-reply-to references subject
198
- return-path content-type mime-version
199
- content-transfer-encoding content-description
200
- content-id content-disposition content-location]
180
+
201
181
 
202
182
  def encoded
203
- buffer = ''
183
+ buffer = String.new
204
184
  buffer.force_encoding('us-ascii') if buffer.respond_to?(:force_encoding)
205
185
  fields.each do |field|
206
186
  buffer << field.encoded
@@ -211,78 +191,47 @@ module Mail
211
191
  def to_s
212
192
  encoded
213
193
  end
214
-
194
+
215
195
  def decoded
216
- 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.'
217
197
  end
218
198
 
219
199
  def field_summary
220
- fields.map { |f| "<#{f.name}: #{f.value}>" }.join(", ")
200
+ fields.summary
221
201
  end
222
202
 
223
203
  # Returns true if the header has a Message-ID defined (empty or not)
224
204
  def has_message_id?
225
- !fields.select { |f| f.responsible_for?('Message-ID') }.empty?
205
+ fields.has_field? 'Message-ID'
226
206
  end
227
207
 
228
208
  # Returns true if the header has a Content-ID defined (empty or not)
229
209
  def has_content_id?
230
- !fields.select { |f| f.responsible_for?('Content-ID') }.empty?
210
+ fields.has_field? 'Content-ID'
231
211
  end
232
212
 
233
213
  # Returns true if the header has a Date defined (empty or not)
234
214
  def has_date?
235
- !fields.select { |f| f.responsible_for?('Date') }.empty?
215
+ fields.has_field? 'Date'
236
216
  end
237
217
 
238
218
  # Returns true if the header has a MIME version defined (empty or not)
239
219
  def has_mime_version?
240
- !fields.select { |f| f.responsible_for?('Mime-Version') }.empty?
220
+ fields.has_field? 'Mime-Version'
241
221
  end
242
222
 
243
223
  private
244
-
245
- def raw_source=(val)
246
- @raw_source = val
247
- end
248
-
249
- # 2.2.3. Long Header Fields
250
- #
251
- # The process of moving from this folded multiple-line representation
252
- # of a header field to its single line representation is called
253
- # "unfolding". Unfolding is accomplished by simply removing any CRLF
254
- # that is immediately followed by WSP. Each header field should be
255
- # treated in its unfolded form for further syntactic and semantic
256
- # evaluation.
257
- def unfold(string)
258
- string.gsub(/#{CRLF}#{WSP}+/, ' ').gsub(/#{WSP}+/, ' ')
259
- end
260
-
261
- # Returns the header with all the folds removed
262
- def unfolded_header
263
- @unfolded_header ||= unfold(raw_source)
264
- end
265
-
224
+
266
225
  # Splits an unfolded and line break cleaned header into individual field
267
226
  # strings.
268
227
  def split_header
269
- self.fields = unfolded_header.split(CRLF)
270
- end
271
-
272
- def select_field_for(name)
273
- fields.select { |f| f.responsible_for?(name) }
274
- end
275
-
276
- def limited_field?(name)
277
- LIMITED_FIELDS.include?(name.to_s.downcase)
228
+ self.fields = @raw_source.split(Constants::HEADER_SPLIT)
278
229
  end
279
230
 
280
- # Enumerable support; yield each field in order to the block if there is one,
281
- # or return an Enumerator for them if there isn't.
282
- def each( &block )
283
- return self.fields.each( &block ) if block
284
- self.fields.each
285
- end
286
231
 
232
+ # Enumerable support. Yield each field in order.
233
+ def each(&block)
234
+ fields.each(&block)
235
+ end
287
236
  end
288
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
@@ -206,6 +199,12 @@ module Mail
206
199
  end
207
200
  end
208
201
 
202
+ # Unregister the given observer, allowing mail to resume operations
203
+ # without it.
204
+ def self.unregister_observer(observer)
205
+ @@delivery_notification_observers.delete(observer)
206
+ end
207
+
209
208
  # You can register an object to be given every mail object that will be sent,
210
209
  # before it is sent. So if you want to add special headers or modify any
211
210
  # email that gets sent through the Mail library, you can do so.
@@ -219,6 +218,12 @@ module Mail
219
218
  end
220
219
  end
221
220
 
221
+ # Unregister the given interceptor, allowing mail to resume operations
222
+ # without it.
223
+ def self.unregister_interceptor(interceptor)
224
+ @@delivery_interceptors.delete(interceptor)
225
+ end
226
+
222
227
  def self.inform_observers(mail)
223
228
  @@delivery_notification_observers.each do |observer|
224
229
  observer.delivered_email(mail)
@@ -233,9 +238,11 @@ module Mail
233
238
 
234
239
  protected
235
240
 
241
+ RANDOM_TAG='%x%x_%x%x%d%x'
242
+
236
243
  def self.random_tag
237
244
  t = Time.now
238
- sprintf('%x%x_%x%x%d%x',
245
+ sprintf(RANDOM_TAG,
239
246
  t.to_i, t.tv_usec,
240
247
  $$, Thread.current.object_id.abs, self.uniq, rand(255))
241
248
  end