mail 2.6.4 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +208 -156
  3. data/lib/mail/attachments_list.rb +13 -10
  4. data/lib/mail/body.rb +96 -107
  5. data/lib/mail/configuration.rb +2 -0
  6. data/lib/mail/constants.rb +27 -5
  7. data/lib/mail/elements/address.rb +61 -50
  8. data/lib/mail/elements/address_list.rb +11 -19
  9. data/lib/mail/elements/content_disposition_element.rb +9 -16
  10. data/lib/mail/elements/content_location_element.rb +6 -11
  11. data/lib/mail/elements/content_transfer_encoding_element.rb +6 -11
  12. data/lib/mail/elements/content_type_element.rb +16 -23
  13. data/lib/mail/elements/date_time_element.rb +7 -15
  14. data/lib/mail/elements/envelope_from_element.rb +22 -23
  15. data/lib/mail/elements/message_ids_element.rb +18 -13
  16. data/lib/mail/elements/mime_version_element.rb +7 -15
  17. data/lib/mail/elements/phrase_list.rb +12 -10
  18. data/lib/mail/elements/received_element.rb +27 -19
  19. data/lib/mail/encodings/7bit.rb +9 -14
  20. data/lib/mail/encodings/8bit.rb +2 -21
  21. data/lib/mail/encodings/base64.rb +11 -12
  22. data/lib/mail/encodings/binary.rb +3 -22
  23. data/lib/mail/encodings/identity.rb +24 -0
  24. data/lib/mail/encodings/quoted_printable.rb +6 -6
  25. data/lib/mail/encodings/transfer_encoding.rb +38 -29
  26. data/lib/mail/encodings/unix_to_unix.rb +4 -2
  27. data/lib/mail/encodings.rb +83 -56
  28. data/lib/mail/envelope.rb +11 -14
  29. data/lib/mail/field.rb +181 -130
  30. data/lib/mail/field_list.rb +61 -8
  31. data/lib/mail/fields/bcc_field.rb +33 -52
  32. data/lib/mail/fields/cc_field.rb +27 -49
  33. data/lib/mail/fields/comments_field.rb +26 -37
  34. data/lib/mail/fields/common_address_field.rb +162 -0
  35. data/lib/mail/fields/common_date_field.rb +56 -0
  36. data/lib/mail/fields/common_field.rb +77 -0
  37. data/lib/mail/fields/common_message_id_field.rb +41 -0
  38. data/lib/mail/fields/content_description_field.rb +6 -14
  39. data/lib/mail/fields/content_disposition_field.rb +11 -38
  40. data/lib/mail/fields/content_id_field.rb +23 -51
  41. data/lib/mail/fields/content_location_field.rb +10 -25
  42. data/lib/mail/fields/content_transfer_encoding_field.rb +30 -31
  43. data/lib/mail/fields/content_type_field.rb +53 -84
  44. data/lib/mail/fields/date_field.rb +22 -52
  45. data/lib/mail/fields/from_field.rb +27 -49
  46. data/lib/mail/fields/in_reply_to_field.rb +37 -49
  47. data/lib/mail/fields/keywords_field.rb +17 -31
  48. data/lib/mail/fields/message_id_field.rb +24 -71
  49. data/lib/mail/fields/mime_version_field.rb +18 -30
  50. data/lib/mail/fields/named_structured_field.rb +10 -0
  51. data/lib/mail/fields/named_unstructured_field.rb +10 -0
  52. data/lib/mail/fields/optional_field.rb +9 -8
  53. data/lib/mail/fields/{common/parameter_hash.rb → parameter_hash.rb} +13 -11
  54. data/lib/mail/fields/received_field.rb +42 -57
  55. data/lib/mail/fields/references_field.rb +34 -49
  56. data/lib/mail/fields/reply_to_field.rb +27 -49
  57. data/lib/mail/fields/resent_bcc_field.rb +27 -49
  58. data/lib/mail/fields/resent_cc_field.rb +27 -49
  59. data/lib/mail/fields/resent_date_field.rb +4 -30
  60. data/lib/mail/fields/resent_from_field.rb +27 -49
  61. data/lib/mail/fields/resent_message_id_field.rb +4 -29
  62. data/lib/mail/fields/resent_sender_field.rb +26 -56
  63. data/lib/mail/fields/resent_to_field.rb +27 -49
  64. data/lib/mail/fields/return_path_field.rb +49 -54
  65. data/lib/mail/fields/sender_field.rb +33 -55
  66. data/lib/mail/fields/structured_field.rb +2 -30
  67. data/lib/mail/fields/subject_field.rb +8 -11
  68. data/lib/mail/fields/to_field.rb +27 -49
  69. data/lib/mail/fields/unstructured_field.rb +31 -47
  70. data/lib/mail/fields.rb +9 -0
  71. data/lib/mail/header.rb +71 -110
  72. data/lib/mail/mail.rb +34 -37
  73. data/lib/mail/matchers/attachment_matchers.rb +15 -0
  74. data/lib/mail/matchers/has_sent_mail.rb +21 -1
  75. data/lib/mail/message.rb +126 -127
  76. data/lib/mail/multibyte/chars.rb +24 -181
  77. data/lib/mail/multibyte/unicode.rb +11 -11
  78. data/lib/mail/multibyte/utils.rb +26 -43
  79. data/lib/mail/multibyte.rb +55 -16
  80. data/lib/mail/network/delivery_methods/exim.rb +8 -11
  81. data/lib/mail/network/delivery_methods/file_delivery.rb +15 -18
  82. data/lib/mail/network/delivery_methods/logger_delivery.rb +34 -0
  83. data/lib/mail/network/delivery_methods/sendmail.rb +32 -35
  84. data/lib/mail/network/delivery_methods/smtp.rb +125 -68
  85. data/lib/mail/network/delivery_methods/smtp_connection.rb +11 -16
  86. data/lib/mail/network/delivery_methods/test_mailer.rb +12 -13
  87. data/lib/mail/network/retriever_methods/base.rb +13 -13
  88. data/lib/mail/network/retriever_methods/imap.rb +25 -9
  89. data/lib/mail/network/retriever_methods/pop3.rb +25 -23
  90. data/lib/mail/network/retriever_methods/test_retriever.rb +3 -2
  91. data/lib/mail/network.rb +1 -0
  92. data/lib/mail/parser_tools.rb +15 -0
  93. data/lib/mail/parsers/address_lists_parser.rb +33228 -116
  94. data/lib/mail/parsers/address_lists_parser.rl +183 -0
  95. data/lib/mail/parsers/content_disposition_parser.rb +885 -49
  96. data/lib/mail/parsers/content_disposition_parser.rl +93 -0
  97. data/lib/mail/parsers/content_location_parser.rb +812 -23
  98. data/lib/mail/parsers/content_location_parser.rl +82 -0
  99. data/lib/mail/parsers/content_transfer_encoding_parser.rb +512 -21
  100. data/lib/mail/parsers/content_transfer_encoding_parser.rl +75 -0
  101. data/lib/mail/parsers/content_type_parser.rb +1039 -55
  102. data/lib/mail/parsers/content_type_parser.rl +94 -0
  103. data/lib/mail/parsers/date_time_parser.rb +880 -25
  104. data/lib/mail/parsers/date_time_parser.rl +73 -0
  105. data/lib/mail/parsers/envelope_from_parser.rb +3672 -40
  106. data/lib/mail/parsers/envelope_from_parser.rl +93 -0
  107. data/lib/mail/parsers/message_ids_parser.rb +5149 -25
  108. data/lib/mail/parsers/message_ids_parser.rl +97 -0
  109. data/lib/mail/parsers/mime_version_parser.rb +500 -26
  110. data/lib/mail/parsers/mime_version_parser.rl +72 -0
  111. data/lib/mail/parsers/phrase_lists_parser.rb +873 -22
  112. data/lib/mail/parsers/phrase_lists_parser.rl +94 -0
  113. data/lib/mail/parsers/received_parser.rb +8779 -43
  114. data/lib/mail/parsers/received_parser.rl +95 -0
  115. data/lib/mail/parsers/rfc2045_content_transfer_encoding.rl +13 -0
  116. data/lib/mail/parsers/rfc2045_content_type.rl +25 -0
  117. data/lib/mail/parsers/rfc2045_mime.rl +16 -0
  118. data/lib/mail/parsers/rfc2183_content_disposition.rl +15 -0
  119. data/lib/mail/parsers/rfc3629_utf8.rl +19 -0
  120. data/lib/mail/parsers/rfc5234_abnf_core_rules.rl +22 -0
  121. data/lib/mail/parsers/rfc5322.rl +74 -0
  122. data/lib/mail/parsers/rfc5322_address.rl +72 -0
  123. data/lib/mail/parsers/{ragel/date_time.rl → rfc5322_date_time.rl} +8 -1
  124. data/lib/mail/parsers/rfc5322_lexical_tokens.rl +60 -0
  125. data/lib/mail/parsers.rb +11 -25
  126. data/lib/mail/part.rb +25 -29
  127. data/lib/mail/parts_list.rb +62 -6
  128. data/lib/mail/smtp_envelope.rb +57 -0
  129. data/lib/mail/utilities.rb +361 -74
  130. data/lib/mail/version.rb +2 -2
  131. data/lib/mail/yaml.rb +30 -0
  132. data/lib/mail.rb +4 -37
  133. metadata +125 -67
  134. data/CHANGELOG.rdoc +0 -787
  135. data/CONTRIBUTING.md +0 -60
  136. data/Dependencies.txt +0 -2
  137. data/Gemfile +0 -11
  138. data/Rakefile +0 -29
  139. data/TODO.rdoc +0 -9
  140. data/lib/mail/check_delivery_params.rb +0 -21
  141. data/lib/mail/core_extensions/smtp.rb +0 -25
  142. data/lib/mail/core_extensions/string/access.rb +0 -146
  143. data/lib/mail/core_extensions/string/multibyte.rb +0 -79
  144. data/lib/mail/core_extensions/string.rb +0 -21
  145. data/lib/mail/fields/common/address_container.rb +0 -17
  146. data/lib/mail/fields/common/common_address.rb +0 -136
  147. data/lib/mail/fields/common/common_date.rb +0 -36
  148. data/lib/mail/fields/common/common_field.rb +0 -61
  149. data/lib/mail/fields/common/common_message_id.rb +0 -49
  150. data/lib/mail/multibyte/exceptions.rb +0 -9
  151. data/lib/mail/parsers/ragel/common.rl +0 -185
  152. data/lib/mail/parsers/ragel/parser_info.rb +0 -61
  153. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +0 -14864
  154. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +0 -37
  155. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +0 -751
  156. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +0 -37
  157. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +0 -614
  158. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +0 -37
  159. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +0 -447
  160. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +0 -37
  161. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +0 -825
  162. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +0 -37
  163. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +0 -817
  164. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +0 -37
  165. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +0 -2149
  166. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +0 -37
  167. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +0 -1570
  168. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +0 -37
  169. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +0 -440
  170. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +0 -37
  171. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +0 -564
  172. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +0 -37
  173. data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +0 -51
  174. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +0 -5144
  175. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +0 -37
  176. data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +0 -37
  177. data/lib/mail/parsers/ragel/ruby.rb +0 -40
  178. data/lib/mail/parsers/ragel.rb +0 -18
  179. data/lib/mail/version_specific/ruby_1_8.rb +0 -126
  180. data/lib/mail/version_specific/ruby_1_9.rb +0 -223
@@ -1,13 +1,12 @@
1
1
  # encoding: utf-8
2
2
  # frozen_string_literal: true
3
- require 'mail/fields/common/common_field'
4
3
 
5
4
  module Mail
6
5
  # Provides access to a structured header field
7
6
  #
8
7
  # ===Per RFC 2822:
9
8
  # 2.2.2. Structured Header Field Bodies
10
- #
9
+ #
11
10
  # Some field bodies in this standard have specific syntactical
12
11
  # structure more restrictive than the unstructured field bodies
13
12
  # described above. These are referred to as "structured" field bodies.
@@ -20,33 +19,6 @@ module Mail
20
19
  # characters are subject to header "folding" and "unfolding" as
21
20
  # described in section 2.2.3. Semantic analysis of structured field
22
21
  # bodies is given along with their syntax.
23
- class StructuredField
24
-
25
- include Mail::CommonField
26
- include Mail::Utilities
27
-
28
- def initialize(name = nil, value = nil, charset = nil)
29
- self.name = name
30
- self.value = value
31
- self.charset = charset
32
- self
33
- end
34
-
35
- def charset
36
- @charset
37
- end
38
-
39
- def charset=(val)
40
- @charset = val
41
- end
42
-
43
- def default
44
- decoded
45
- end
46
-
47
- def errors
48
- []
49
- end
50
-
22
+ class StructuredField < CommonField #:nodoc:
51
23
  end
52
24
  end
@@ -1,17 +1,14 @@
1
1
  # encoding: utf-8
2
2
  # frozen_string_literal: true
3
- #
4
- # subject = "Subject:" unstructured CRLF
3
+
5
4
  module Mail
6
- class SubjectField < UnstructuredField
7
-
8
- FIELD_NAME = 'subject'
9
- CAPITALIZED_FIELD = "Subject"
10
-
11
- def initialize(value = nil, charset = 'utf-8')
12
- self.charset = charset
13
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
5
+ #
6
+ # subject = "Subject:" unstructured CRLF
7
+ class SubjectField < NamedUnstructuredField #:nodoc:
8
+ NAME = 'Subject'
9
+
10
+ def self.singular?
11
+ true
14
12
  end
15
-
16
13
  end
17
14
  end
@@ -1,55 +1,33 @@
1
1
  # encoding: utf-8
2
2
  # frozen_string_literal: true
3
- #
4
- # = To Field
5
- #
6
- # The To field inherits to StructuredField and handles the To: header
7
- # field in the email.
8
- #
9
- # Sending to to a mail message will instantiate a Mail::Field object that
10
- # has a ToField as its field type. This includes all Mail::CommonAddress
11
- # module instance metods.
12
- #
13
- # Only one To field can appear in a header, though it can have multiple
14
- # addresses and groups of addresses.
15
- #
16
- # == Examples:
17
- #
18
- # mail = Mail.new
19
- # mail.to = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
20
- # mail.to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
21
- # mail[:to] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ToField:0x180e1c4
22
- # mail['to'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ToField:0x180e1c4
23
- # mail['To'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ToField:0x180e1c4
24
- #
25
- # mail[:to].encoded #=> 'To: Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net\r\n'
26
- # mail[:to].decoded #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
27
- # mail[:to].addresses #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
28
- # mail[:to].formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
29
- #
30
- require 'mail/fields/common/common_address'
31
3
 
32
4
  module Mail
33
- class ToField < StructuredField
34
-
35
- include Mail::CommonAddress
36
-
37
- FIELD_NAME = 'to'
38
- CAPITALIZED_FIELD = 'To'
39
-
40
- def initialize(value = nil, charset = 'utf-8')
41
- self.charset = charset
42
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
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
-
5
+ # = To Field
6
+ #
7
+ # The To field inherits to StructuredField and handles the To: header
8
+ # field in the email.
9
+ #
10
+ # Sending to to a mail message will instantiate a Mail::Field object that
11
+ # has a ToField as its field type. This includes all Mail::CommonAddress
12
+ # module instance metods.
13
+ #
14
+ # Only one To field can appear in a header, though it can have multiple
15
+ # addresses and groups of addresses.
16
+ #
17
+ # == Examples:
18
+ #
19
+ # mail = Mail.new
20
+ # mail.to = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
21
+ # mail.to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
22
+ # mail[:to] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ToField:0x180e1c4
23
+ # mail['to'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ToField:0x180e1c4
24
+ # mail['To'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ToField:0x180e1c4
25
+ #
26
+ # mail[:to].encoded #=> 'To: Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net\r\n'
27
+ # mail[:to].decoded #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
28
+ # mail[:to].addresses #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
29
+ # mail[:to].formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
30
+ class ToField < CommonAddressField #:nodoc:
31
+ NAME = 'To'
54
32
  end
55
33
  end
@@ -1,6 +1,6 @@
1
1
  # encoding: utf-8
2
2
  # frozen_string_literal: true
3
- require 'mail/fields/common/common_field'
3
+ require 'mail/utilities'
4
4
 
5
5
  module Mail
6
6
  # Provides access to an unstructured header field
@@ -15,59 +15,39 @@ module Mail
15
15
  # field bodies are simply to be treated as a single line of characters
16
16
  # with no further processing (except for header "folding" and
17
17
  # "unfolding" as described in section 2.2.3).
18
- class UnstructuredField
19
-
20
- include Mail::CommonField
21
- include Mail::Utilities
22
-
23
- attr_accessor :charset
24
- attr_reader :errors
25
-
18
+ class UnstructuredField < CommonField #:nodoc:
26
19
  def initialize(name, value, charset = nil)
27
- @errors = []
28
-
29
20
  if value.is_a?(Array)
30
21
  # Probably has arrived here from a failed parse of an AddressList Field
31
22
  value = value.join(', ')
32
- else
33
- # Ensure we are dealing with a string
34
- value = value.to_s
23
+
24
+ # Mark UTF-8 strings parsed from ASCII-8BIT
25
+ elsif value.respond_to?(:force_encoding) && value.encoding == Encoding::ASCII_8BIT
26
+ utf8 = value.dup.force_encoding(Encoding::UTF_8)
27
+ value = utf8 if utf8.valid_encoding?
35
28
  end
36
29
 
37
- if charset
38
- self.charset = charset
39
- else
30
+ charset ||=
40
31
  if value.respond_to?(:encoding)
41
- self.charset = value.encoding
42
- else
43
- self.charset = $KCODE
32
+ value.encoding
44
33
  end
45
- end
46
- self.name = name
47
- self.value = value
48
- self
49
- end
50
34
 
51
- def encoded
52
- do_encode
35
+ super name, value.to_s, charset
53
36
  end
54
37
 
55
- def decoded
56
- do_decode
57
- end
58
-
59
- def default
60
- decoded
61
- end
62
-
63
- def parse # An unstructured field does not parse
38
+ # An unstructured field does not parse
39
+ def parse
64
40
  self
65
41
  end
66
42
 
67
43
  private
68
44
 
69
45
  def do_encode
70
- value.nil? ? '' : "#{wrapped_value}\r\n"
46
+ if value && !value.empty?
47
+ "#{wrapped_value}\r\n"
48
+ else
49
+ ''
50
+ end
71
51
  end
72
52
 
73
53
  def do_decode
@@ -121,7 +101,7 @@ module Mail
121
101
  def fold(prepend = 0) # :nodoc:
122
102
  encoding = normalized_encoding
123
103
  decoded_string = decoded.to_s
124
- should_encode = decoded_string.not_ascii_only?
104
+ should_encode = !decoded_string.ascii_only?
125
105
  if should_encode
126
106
  first = true
127
107
  words = decoded_string.split(/[ \t]/).map do |word|
@@ -130,7 +110,7 @@ module Mail
130
110
  else
131
111
  word = " #{word}"
132
112
  end
133
- if word.not_ascii_only?
113
+ if !word.ascii_only?
134
114
  word
135
115
  else
136
116
  word.scan(/.{7}|.+$/)
@@ -148,7 +128,14 @@ module Mail
148
128
  first_word = true
149
129
  while !words.empty?
150
130
  break unless word = words.first.dup
151
- word.encode!(charset) if charset && word.respond_to?(:encode!)
131
+
132
+ # Convert on 1.9+ only since we aren't sure of the current
133
+ # charset encoding on 1.8. We'd need to track internal/external
134
+ # charset on each field.
135
+ if charset && word.respond_to?(:encoding)
136
+ word = Encodings.transcode_charset(word, word.encoding, charset)
137
+ end
138
+
152
139
  word = encode(word) if should_encode
153
140
  word = encode_crlf(word)
154
141
  # Skip to next line if we're going to go past the limit
@@ -179,7 +166,7 @@ module Mail
179
166
  end
180
167
 
181
168
  def encode(value)
182
- value = [value].pack(CAPITAL_M).gsub(EQUAL_LF, EMPTY)
169
+ value = [value].pack(Constants::CAPITAL_M).gsub(Constants::EQUAL_LF, Constants::EMPTY)
183
170
  value.gsub!(/"/, '=22')
184
171
  value.gsub!(/\(/, '=28')
185
172
  value.gsub!(/\)/, '=29')
@@ -190,16 +177,13 @@ module Mail
190
177
  end
191
178
 
192
179
  def encode_crlf(value)
193
- value.gsub!(CR, CR_ENCODED)
194
- value.gsub!(LF, LF_ENCODED)
180
+ value.gsub!(Constants::CR, Constants::CR_ENCODED)
181
+ value.gsub!(Constants::LF, Constants::LF_ENCODED)
195
182
  value
196
183
  end
197
184
 
198
185
  def normalized_encoding
199
- encoding = charset.to_s.upcase.gsub('_', '-')
200
- encoding = 'UTF-8' if encoding == 'UTF8' # Ruby 1.8.x and $KCODE == 'u'
201
- encoding
186
+ charset.to_s.upcase.gsub('_', '-')
202
187
  end
203
-
204
188
  end
205
189
  end
data/lib/mail/fields.rb CHANGED
@@ -1,6 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
  module Mail
3
+ register_autoload :CommonField, 'mail/fields/common_field'
4
+ register_autoload :CommonAddressField, 'mail/fields/common_address_field'
5
+ register_autoload :CommonMessageIdField, 'mail/fields/common_message_id_field'
6
+ register_autoload :CommonDateField, 'mail/fields/common_date_field'
7
+
3
8
  register_autoload :UnstructuredField, 'mail/fields/unstructured_field'
9
+ register_autoload :NamedUnstructuredField, 'mail/fields/named_unstructured_field'
4
10
  register_autoload :StructuredField, 'mail/fields/structured_field'
5
11
  register_autoload :OptionalField, 'mail/fields/optional_field'
6
12
 
@@ -19,6 +25,7 @@ module Mail
19
25
  register_autoload :KeywordsField, 'mail/fields/keywords_field'
20
26
  register_autoload :MessageIdField, 'mail/fields/message_id_field'
21
27
  register_autoload :MimeVersionField, 'mail/fields/mime_version_field'
28
+ register_autoload :NamedStructuredField, 'mail/fields/named_structured_field'
22
29
  register_autoload :ReceivedField, 'mail/fields/received_field'
23
30
  register_autoload :ReferencesField, 'mail/fields/references_field'
24
31
  register_autoload :ReplyToField, 'mail/fields/reply_to_field'
@@ -33,4 +40,6 @@ module Mail
33
40
  register_autoload :SenderField, 'mail/fields/sender_field'
34
41
  register_autoload :SubjectField, 'mail/fields/subject_field'
35
42
  register_autoload :ToField, 'mail/fields/to_field'
43
+
44
+ register_autoload :ParameterHash, 'mail/fields/parameter_hash'
36
45
  end
data/lib/mail/header.rb CHANGED
@@ -1,13 +1,15 @@
1
1
  # encoding: utf-8
2
2
  # frozen_string_literal: true
3
+ require 'mail/constants'
4
+ require 'mail/utilities'
5
+
3
6
  module Mail
4
-
5
7
  # Provides access to a header object.
6
- #
8
+ #
7
9
  # ===Per RFC2822
8
- #
10
+ #
9
11
  # 2.2. Header Fields
10
- #
12
+ #
11
13
  # Header fields are lines composed of a field name, followed by a colon
12
14
  # (":"), followed by a field body, and terminated by CRLF. A field
13
15
  # name MUST be composed of printable US-ASCII characters (i.e.,
@@ -18,14 +20,12 @@ module Mail
18
20
  # 2.2.3. All field bodies MUST conform to the syntax described in
19
21
  # sections 3 and 4 of this standard.
20
22
  class Header
21
- include Constants
22
- include Utilities
23
23
  include Enumerable
24
-
24
+
25
25
  @@maximum_amount = 1000
26
26
 
27
27
  # Large amount of headers in Email might create extra high CPU load
28
- # 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
29
29
  # mail library.
30
30
  # Default: 1000
31
31
  def self.maximum_amount
@@ -36,12 +36,14 @@ module Mail
36
36
  @@maximum_amount = value
37
37
  end
38
38
 
39
+ attr_reader :raw_source, :charset
40
+
39
41
  # Creates a new header object.
40
- #
42
+ #
41
43
  # Accepts raw text or nothing. If given raw text will attempt to parse
42
44
  # it and split it into the various fields, instantiating each field as
43
45
  # it goes.
44
- #
46
+ #
45
47
  # If it finds a field that should be a structured field (such as content
46
48
  # type), but it fails to parse it, it will simply make it an unstructured
47
49
  # field and leave it alone. This will mean that the data is preserved but
@@ -50,29 +52,24 @@ module Mail
50
52
  # me the example so we can fix it.
51
53
  def initialize(header_text = nil, charset = nil)
52
54
  @charset = charset
53
- self.raw_source = ::Mail::Utilities.to_crlf(header_text).lstrip
55
+ @raw_source = ::Mail::Utilities.to_crlf(header_text).lstrip
54
56
  split_header if header_text
55
57
  end
56
58
 
57
59
  def initialize_copy(original)
58
60
  super
59
61
  @fields = @fields.dup
62
+ @fields.map!(&:dup)
60
63
  end
61
-
62
- # The preserved raw source of the header as you passed it in, untouched
63
- # for your Regexing glory.
64
- def raw_source
65
- @raw_source
66
- end
67
-
64
+
68
65
  # Returns an array of all the fields in the header in order that they
69
66
  # were read in.
70
67
  def fields
71
68
  @fields ||= FieldList.new
72
69
  end
73
-
70
+
74
71
  # 3.6. Field definitions
75
- #
72
+ #
76
73
  # It is important to note that the header fields are not guaranteed to
77
74
  # be in a particular order. They may appear in any order, and they
78
75
  # have been known to be reordered occasionally when transported over
@@ -82,35 +79,35 @@ module Mail
82
79
  # header fields MUST NOT be reordered, and SHOULD be kept in blocks
83
80
  # prepended to the message. See sections 3.6.6 and 3.6.7 for more
84
81
  # information.
85
- #
82
+ #
86
83
  # Populates the fields container with Field objects in the order it
87
84
  # receives them in.
88
85
  #
89
86
  # Acceps an array of field string values, for example:
90
- #
87
+ #
91
88
  # h = Header.new
92
89
  # h.fields = ['From: mikel@me.com', 'To: bob@you.com']
93
90
  def fields=(unfolded_fields)
94
91
  @fields = Mail::FieldList.new
95
- 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
96
- unfolded_fields[0..(self.class.maximum_amount-1)].each do |field|
97
92
 
98
- field = Field.new(field, nil, charset)
99
- if limited_field?(field.name) && (selected = select_field_for(field.name)) && selected.any?
100
- selected.first.update(field.name, field.value)
101
- else
102
- @fields << field
103
- 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)
104
96
  end
105
97
 
98
+ unfolded_fields.each do |field|
99
+ if field = Field.parse(field, charset)
100
+ @fields.add_field field
101
+ end
102
+ end
106
103
  end
107
-
104
+
108
105
  def errors
109
106
  @fields.map(&:errors).flatten(1)
110
107
  end
111
-
108
+
112
109
  # 3.6. Field definitions
113
- #
110
+ #
114
111
  # The following table indicates limits on the number of times each
115
112
  # field may occur in a message header as well as any special
116
113
  # limitations on the use of those fields. An asterisk next to a value
@@ -120,35 +117,25 @@ module Mail
120
117
  # <snip table from 3.6>
121
118
  #
122
119
  # As per RFC, many fields can appear more than once, we will return a string
123
- # 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
124
121
  # matching header, will return an array of values in order that they appear
125
122
  # in the header ordered from top to bottom.
126
- #
123
+ #
127
124
  # Example:
128
- #
125
+ #
129
126
  # h = Header.new
130
127
  # h.fields = ['To: mikel@me.com', 'X-Mail-SPAM: 15', 'X-Mail-SPAM: 20']
131
128
  # h['To'] #=> 'mikel@me.com'
132
129
  # h['X-Mail-SPAM'] #=> ['15', '20']
133
130
  def [](name)
134
- name = dasherize(name)
135
- name.downcase!
136
- selected = select_field_for(name)
137
- case
138
- when selected.length > 1
139
- selected.map { |f| f }
140
- when !Utilities.blank?(selected)
141
- selected.first
142
- else
143
- nil
144
- end
131
+ fields.get_field(Utilities.dasherize(name))
145
132
  end
146
-
133
+
147
134
  # Sets the FIRST matching field in the header to passed value, or deletes
148
135
  # the FIRST field matched from the header if passed nil
149
- #
136
+ #
150
137
  # Example:
151
- #
138
+ #
152
139
  # h = Header.new
153
140
  # h.fields = ['To: mikel@me.com', 'X-Mail-SPAM: 15', 'X-Mail-SPAM: 20']
154
141
  # h['To'] = 'bob@you.com'
@@ -158,51 +145,39 @@ module Mail
158
145
  # h['X-Mail-SPAM'] = nil
159
146
  # h['X-Mail-SPAM'] # => nil
160
147
  def []=(name, value)
161
- name = dasherize(name)
162
- if name.include?(':')
148
+ name = name.to_s
149
+ if name.include?(Constants::COLON)
163
150
  raise ArgumentError, "Header names may not contain a colon: #{name.inspect}"
164
151
  end
165
- fn = name.downcase
166
- selected = select_field_for(fn)
167
-
168
- case
169
- # User wants to delete the field
170
- when !Utilities.blank?(selected) && value == nil
171
- fields.delete_if { |f| selected.include?(f) }
172
-
173
- # User wants to change the field
174
- when !Utilities.blank?(selected) && limited_field?(fn)
175
- selected.first.update(fn, value)
176
-
177
- # 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
178
158
  else
179
- # Need to insert in correct order for trace fields
180
- self.fields << Field.new(name.to_s, value, charset)
181
- end
182
- if dasherize(fn) == "content-type"
159
+ fields.add_field Field.new(name.to_s, value, charset)
160
+
183
161
  # Update charset if specified in Content-Type
184
- params = self[:content_type].parameters rescue nil
185
- @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
186
166
  end
187
167
  end
188
-
189
- def charset
190
- @charset
191
- end
192
-
168
+
193
169
  def charset=(val)
194
170
  params = self[:content_type].parameters rescue nil
195
171
  if params
196
- params[:charset] = val
172
+ if val
173
+ params[:charset] = val
174
+ else
175
+ params.delete(:charset)
176
+ end
197
177
  end
198
178
  @charset = val
199
179
  end
200
-
201
- LIMITED_FIELDS = %w[ date from sender reply-to to cc bcc
202
- message-id in-reply-to references subject
203
- return-path content-type mime-version
204
- content-transfer-encoding content-description
205
- content-id content-disposition content-location]
180
+
206
181
 
207
182
  def encoded
208
183
  buffer = String.new
@@ -216,61 +191,47 @@ module Mail
216
191
  def to_s
217
192
  encoded
218
193
  end
219
-
194
+
220
195
  def decoded
221
- 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.'
222
197
  end
223
198
 
224
199
  def field_summary
225
- fields.map { |f| "<#{f.name}: #{f.value}>" }.join(", ")
200
+ fields.summary
226
201
  end
227
202
 
228
203
  # Returns true if the header has a Message-ID defined (empty or not)
229
204
  def has_message_id?
230
- !fields.select { |f| f.responsible_for?('Message-ID') }.empty?
205
+ fields.has_field? 'Message-ID'
231
206
  end
232
207
 
233
208
  # Returns true if the header has a Content-ID defined (empty or not)
234
209
  def has_content_id?
235
- !fields.select { |f| f.responsible_for?('Content-ID') }.empty?
210
+ fields.has_field? 'Content-ID'
236
211
  end
237
212
 
238
213
  # Returns true if the header has a Date defined (empty or not)
239
214
  def has_date?
240
- !fields.select { |f| f.responsible_for?('Date') }.empty?
215
+ fields.has_field? 'Date'
241
216
  end
242
217
 
243
218
  # Returns true if the header has a MIME version defined (empty or not)
244
219
  def has_mime_version?
245
- !fields.select { |f| f.responsible_for?('Mime-Version') }.empty?
220
+ fields.has_field? 'MIME-Version'
246
221
  end
247
222
 
248
223
  private
249
-
250
- def raw_source=(val)
251
- @raw_source = val
252
- end
253
-
224
+
254
225
  # Splits an unfolded and line break cleaned header into individual field
255
226
  # strings.
256
227
  def split_header
257
- self.fields = raw_source.split(HEADER_SPLIT)
258
- end
259
-
260
- def select_field_for(name)
261
- fields.select { |f| f.responsible_for?(name) }
262
- end
263
-
264
- def limited_field?(name)
265
- LIMITED_FIELDS.include?(name.to_s.downcase)
228
+ self.fields = @raw_source.split(Constants::HEADER_SPLIT)
266
229
  end
267
230
 
268
- # Enumerable support; yield each field in order to the block if there is one,
269
- # or return an Enumerator for them if there isn't.
270
- def each( &block )
271
- return self.fields.each( &block ) if block
272
- self.fields.each
273
- end
274
231
 
232
+ # Enumerable support. Yield each field in order.
233
+ def each(&block)
234
+ fields.each(&block)
235
+ end
275
236
  end
276
237
  end