mail 2.7.1 → 2.8.0.rc1

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 (122) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +45 -28
  3. data/lib/mail/attachments_list.rb +2 -5
  4. data/lib/mail/body.rb +24 -47
  5. data/lib/mail/constants.rb +27 -5
  6. data/lib/mail/elements/address.rb +27 -27
  7. data/lib/mail/elements/address_list.rb +1 -1
  8. data/lib/mail/elements/content_disposition_element.rb +1 -1
  9. data/lib/mail/elements/content_location_element.rb +1 -1
  10. data/lib/mail/elements/content_transfer_encoding_element.rb +1 -1
  11. data/lib/mail/elements/content_type_element.rb +8 -4
  12. data/lib/mail/elements/date_time_element.rb +1 -1
  13. data/lib/mail/elements/envelope_from_element.rb +13 -7
  14. data/lib/mail/elements/message_ids_element.rb +14 -5
  15. data/lib/mail/elements/mime_version_element.rb +1 -1
  16. data/lib/mail/elements/phrase_list.rb +7 -2
  17. data/lib/mail/elements/received_element.rb +20 -6
  18. data/lib/mail/encodings/7bit.rb +5 -0
  19. data/lib/mail/encodings/base64.rb +2 -2
  20. data/lib/mail/encodings/quoted_printable.rb +2 -2
  21. data/lib/mail/encodings.rb +30 -59
  22. data/lib/mail/envelope.rb +11 -14
  23. data/lib/mail/field.rb +37 -53
  24. data/lib/mail/field_list.rb +60 -7
  25. data/lib/mail/fields/bcc_field.rb +34 -52
  26. data/lib/mail/fields/cc_field.rb +28 -49
  27. data/lib/mail/fields/comments_field.rb +27 -37
  28. data/lib/mail/fields/common_address_field.rb +170 -0
  29. data/lib/mail/fields/common_date_field.rb +58 -0
  30. data/lib/mail/fields/common_field.rb +77 -0
  31. data/lib/mail/fields/common_message_id_field.rb +42 -0
  32. data/lib/mail/fields/content_description_field.rb +7 -14
  33. data/lib/mail/fields/content_disposition_field.rb +13 -38
  34. data/lib/mail/fields/content_id_field.rb +24 -51
  35. data/lib/mail/fields/content_location_field.rb +11 -25
  36. data/lib/mail/fields/content_transfer_encoding_field.rb +31 -31
  37. data/lib/mail/fields/content_type_field.rb +46 -71
  38. data/lib/mail/fields/date_field.rb +23 -51
  39. data/lib/mail/fields/from_field.rb +28 -49
  40. data/lib/mail/fields/in_reply_to_field.rb +38 -49
  41. data/lib/mail/fields/keywords_field.rb +18 -31
  42. data/lib/mail/fields/message_id_field.rb +25 -71
  43. data/lib/mail/fields/mime_version_field.rb +19 -30
  44. data/lib/mail/fields/named_structured_field.rb +11 -0
  45. data/lib/mail/fields/named_unstructured_field.rb +11 -0
  46. data/lib/mail/fields/optional_field.rb +5 -6
  47. data/lib/mail/fields/{common/parameter_hash.rb → parameter_hash.rb} +12 -10
  48. data/lib/mail/fields/received_field.rb +43 -57
  49. data/lib/mail/fields/references_field.rb +35 -49
  50. data/lib/mail/fields/reply_to_field.rb +28 -49
  51. data/lib/mail/fields/resent_bcc_field.rb +28 -49
  52. data/lib/mail/fields/resent_cc_field.rb +28 -49
  53. data/lib/mail/fields/resent_date_field.rb +5 -29
  54. data/lib/mail/fields/resent_from_field.rb +28 -49
  55. data/lib/mail/fields/resent_message_id_field.rb +5 -29
  56. data/lib/mail/fields/resent_sender_field.rb +27 -56
  57. data/lib/mail/fields/resent_to_field.rb +28 -49
  58. data/lib/mail/fields/return_path_field.rb +50 -54
  59. data/lib/mail/fields/sender_field.rb +34 -55
  60. data/lib/mail/fields/structured_field.rb +3 -30
  61. data/lib/mail/fields/subject_field.rb +9 -11
  62. data/lib/mail/fields/to_field.rb +28 -49
  63. data/lib/mail/fields/unstructured_field.rb +16 -48
  64. data/lib/mail/header.rb +69 -110
  65. data/lib/mail/matchers/attachment_matchers.rb +15 -0
  66. data/lib/mail/message.rb +46 -64
  67. data/lib/mail/multibyte/chars.rb +8 -166
  68. data/lib/mail/multibyte/utils.rb +26 -43
  69. data/lib/mail/multibyte.rb +1 -11
  70. data/lib/mail/network/delivery_methods/exim.rb +5 -4
  71. data/lib/mail/network/delivery_methods/file_delivery.rb +11 -10
  72. data/lib/mail/network/delivery_methods/logger_delivery.rb +2 -5
  73. data/lib/mail/network/delivery_methods/sendmail.rb +27 -35
  74. data/lib/mail/network/delivery_methods/smtp.rb +3 -3
  75. data/lib/mail/network/delivery_methods/smtp_connection.rb +3 -12
  76. data/lib/mail/network/delivery_methods/test_mailer.rb +4 -2
  77. data/lib/mail/network/retriever_methods/base.rb +8 -8
  78. data/lib/mail/network/retriever_methods/imap.rb +2 -2
  79. data/lib/mail/network/retriever_methods/pop3.rb +2 -2
  80. data/lib/mail/network/retriever_methods/test_retriever.rb +2 -1
  81. data/lib/mail/parsers/address_lists_parser.rb +33070 -33064
  82. data/lib/mail/parsers/address_lists_parser.rl +7 -0
  83. data/lib/mail/parsers/content_disposition_parser.rb +833 -827
  84. data/lib/mail/parsers/content_disposition_parser.rl +7 -0
  85. data/lib/mail/parsers/content_location_parser.rb +770 -764
  86. data/lib/mail/parsers/content_location_parser.rl +7 -0
  87. data/lib/mail/parsers/content_transfer_encoding_parser.rb +474 -468
  88. data/lib/mail/parsers/content_transfer_encoding_parser.rl +7 -0
  89. data/lib/mail/parsers/content_type_parser.rb +971 -965
  90. data/lib/mail/parsers/content_type_parser.rl +7 -0
  91. data/lib/mail/parsers/date_time_parser.rb +838 -832
  92. data/lib/mail/parsers/date_time_parser.rl +7 -0
  93. data/lib/mail/parsers/envelope_from_parser.rb +3623 -3529
  94. data/lib/mail/parsers/envelope_from_parser.rl +7 -0
  95. data/lib/mail/parsers/message_ids_parser.rb +5107 -2800
  96. data/lib/mail/parsers/message_ids_parser.rl +12 -1
  97. data/lib/mail/parsers/mime_version_parser.rb +463 -457
  98. data/lib/mail/parsers/mime_version_parser.rl +7 -0
  99. data/lib/mail/parsers/phrase_lists_parser.rb +836 -830
  100. data/lib/mail/parsers/phrase_lists_parser.rl +8 -1
  101. data/lib/mail/parsers/received_parser.rb +8688 -8682
  102. data/lib/mail/parsers/received_parser.rl +7 -0
  103. data/lib/mail/parsers/rfc5322.rl +28 -13
  104. data/lib/mail/parsers.rb +11 -17
  105. data/lib/mail/part.rb +5 -9
  106. data/lib/mail/parts_list.rb +57 -0
  107. data/lib/mail/smtp_envelope.rb +57 -0
  108. data/lib/mail/utilities.rb +307 -69
  109. data/lib/mail/version.rb +3 -3
  110. data/lib/mail/yaml.rb +30 -0
  111. data/lib/mail.rb +0 -20
  112. metadata +74 -21
  113. data/lib/mail/check_delivery_params.rb +0 -60
  114. data/lib/mail/core_extensions/smtp.rb +0 -28
  115. data/lib/mail/core_extensions/string.rb +0 -17
  116. data/lib/mail/fields/common/address_container.rb +0 -17
  117. data/lib/mail/fields/common/common_address.rb +0 -161
  118. data/lib/mail/fields/common/common_date.rb +0 -36
  119. data/lib/mail/fields/common/common_field.rb +0 -52
  120. data/lib/mail/fields/common/common_message_id.rb +0 -49
  121. data/lib/mail/version_specific/ruby_1_8.rb +0 -163
  122. data/lib/mail/version_specific/ruby_1_9.rb +0 -278
@@ -1,13 +1,13 @@
1
1
  # encoding: utf-8
2
2
  # frozen_string_literal: true
3
- require 'mail/fields/common/common_field'
3
+ require 'mail/fields/common_field'
4
4
 
5
5
  module Mail
6
6
  # Provides access to a structured header field
7
7
  #
8
8
  # ===Per RFC 2822:
9
9
  # 2.2.2. Structured Header Field Bodies
10
- #
10
+ #
11
11
  # Some field bodies in this standard have specific syntactical
12
12
  # structure more restrictive than the unstructured field bodies
13
13
  # described above. These are referred to as "structured" field bodies.
@@ -20,33 +20,6 @@ module Mail
20
20
  # characters are subject to header "folding" and "unfolding" as
21
21
  # described in section 2.2.3. Semantic analysis of structured field
22
22
  # 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
-
23
+ class StructuredField < CommonField #:nodoc:
51
24
  end
52
25
  end
@@ -1,17 +1,15 @@
1
1
  # encoding: utf-8
2
2
  # frozen_string_literal: true
3
- #
4
- # subject = "Subject:" unstructured CRLF
3
+ require 'mail/fields/named_unstructured_field'
4
+
5
5
  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, value, charset)
6
+ #
7
+ # subject = "Subject:" unstructured CRLF
8
+ class SubjectField < NamedUnstructuredField #:nodoc:
9
+ NAME = 'Subject'
10
+
11
+ def self.singular?
12
+ true
14
13
  end
15
-
16
14
  end
17
15
  end
@@ -1,55 +1,34 @@
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'
3
+ require 'mail/fields/common_address_field'
31
4
 
32
5
  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, 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
-
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,6 +1,7 @@
1
1
  # encoding: utf-8
2
2
  # frozen_string_literal: true
3
- require 'mail/fields/common/common_field'
3
+ require 'mail/fields/common_field'
4
+ require 'mail/utilities'
4
5
 
5
6
  module Mail
6
7
  # Provides access to an unstructured header field
@@ -15,58 +16,28 @@ module Mail
15
16
  # field bodies are simply to be treated as a single line of characters
16
17
  # with no further processing (except for header "folding" and
17
18
  # "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
-
19
+ class UnstructuredField < CommonField #:nodoc:
26
20
  def initialize(name, value, charset = nil)
27
- @errors = []
28
-
29
21
  if value.is_a?(Array)
30
22
  # Probably has arrived here from a failed parse of an AddressList Field
31
23
  value = value.join(', ')
32
- else
33
- # Ensure we are dealing with a string
34
- value = value.to_s
35
24
 
36
- # Mark UTF-8 strings parsed from ASCII-8BIT
37
- if value.respond_to?(:force_encoding) && value.encoding == Encoding::ASCII_8BIT
38
- utf8 = value.dup.force_encoding(Encoding::UTF_8)
39
- value = utf8 if utf8.valid_encoding?
40
- end
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?
41
29
  end
42
30
 
43
- if charset
44
- self.charset = charset
45
- else
31
+ charset ||=
46
32
  if value.respond_to?(:encoding)
47
- self.charset = value.encoding
48
- else
49
- self.charset = $KCODE
33
+ value.encoding
50
34
  end
51
- end
52
- self.name = name
53
- self.value = value
54
- self
55
- end
56
-
57
- def encoded
58
- do_encode
59
- end
60
35
 
61
- def decoded
62
- do_decode
36
+ super name, value.to_s, charset
63
37
  end
64
38
 
65
- def default
66
- decoded
67
- end
68
-
69
- def parse # An unstructured field does not parse
39
+ # An unstructured field does not parse
40
+ def parse
70
41
  self
71
42
  end
72
43
 
@@ -196,7 +167,7 @@ module Mail
196
167
  end
197
168
 
198
169
  def encode(value)
199
- value = [value].pack(CAPITAL_M).gsub(EQUAL_LF, EMPTY)
170
+ value = [value].pack(Constants::CAPITAL_M).gsub(Constants::EQUAL_LF, Constants::EMPTY)
200
171
  value.gsub!(/"/, '=22')
201
172
  value.gsub!(/\(/, '=28')
202
173
  value.gsub!(/\)/, '=29')
@@ -207,16 +178,13 @@ module Mail
207
178
  end
208
179
 
209
180
  def encode_crlf(value)
210
- value.gsub!(CR, CR_ENCODED)
211
- value.gsub!(LF, LF_ENCODED)
181
+ value.gsub!(Constants::CR, Constants::CR_ENCODED)
182
+ value.gsub!(Constants::LF, Constants::LF_ENCODED)
212
183
  value
213
184
  end
214
185
 
215
186
  def normalized_encoding
216
- encoding = charset.to_s.upcase.gsub('_', '-')
217
- encoding = 'UTF-8' if encoding == 'UTF8' # Ruby 1.8.x and $KCODE == 'u'
218
- encoding
187
+ charset.to_s.upcase.gsub('_', '-')
219
188
  end
220
-
221
189
  end
222
190
  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,7 +52,7 @@ 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 = header_text
55
+ @raw_source = ::Mail::Utilities.to_crlf(header_text).lstrip
54
56
  split_header if header_text
55
57
  end
56
58
 
@@ -59,21 +61,15 @@ module Mail
59
61
  @fields = @fields.dup
60
62
  @fields.map!(&:dup)
61
63
  end
62
-
63
- # The preserved raw source of the header as you passed it in, untouched
64
- # for your Regexing glory.
65
- def raw_source
66
- @raw_source
67
- end
68
-
64
+
69
65
  # Returns an array of all the fields in the header in order that they
70
66
  # were read in.
71
67
  def fields
72
68
  @fields ||= FieldList.new
73
69
  end
74
-
70
+
75
71
  # 3.6. Field definitions
76
- #
72
+ #
77
73
  # It is important to note that the header fields are not guaranteed to
78
74
  # be in a particular order. They may appear in any order, and they
79
75
  # have been known to be reordered occasionally when transported over
@@ -83,36 +79,35 @@ module Mail
83
79
  # header fields MUST NOT be reordered, and SHOULD be kept in blocks
84
80
  # prepended to the message. See sections 3.6.6 and 3.6.7 for more
85
81
  # information.
86
- #
82
+ #
87
83
  # Populates the fields container with Field objects in the order it
88
84
  # receives them in.
89
85
  #
90
86
  # Acceps an array of field string values, for example:
91
- #
87
+ #
92
88
  # h = Header.new
93
89
  # h.fields = ['From: mikel@me.com', 'To: bob@you.com']
94
90
  def fields=(unfolded_fields)
95
91
  @fields = Mail::FieldList.new
96
- Kernel.warn "WARNING: More than #{self.class.maximum_amount} header fields; only using the first #{self.class.maximum_amount} and ignoring the rest" if unfolded_fields.length > self.class.maximum_amount
97
- unfolded_fields[0..(self.class.maximum_amount-1)].each do |field|
98
92
 
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)
96
+ end
97
+
98
+ unfolded_fields.each do |field|
99
99
  if field = Field.parse(field, charset)
100
- if limited_field?(field.name) && (selected = select_field_for(field.name)) && selected.any?
101
- selected.first.update(field.name, field.value)
102
- else
103
- @fields << field
104
- end
100
+ @fields.add_field field
105
101
  end
106
102
  end
107
-
108
103
  end
109
-
104
+
110
105
  def errors
111
106
  @fields.map(&:errors).flatten(1)
112
107
  end
113
-
108
+
114
109
  # 3.6. Field definitions
115
- #
110
+ #
116
111
  # The following table indicates limits on the number of times each
117
112
  # field may occur in a message header as well as any special
118
113
  # limitations on the use of those fields. An asterisk next to a value
@@ -122,35 +117,25 @@ module Mail
122
117
  # <snip table from 3.6>
123
118
  #
124
119
  # As per RFC, many fields can appear more than once, we will return a string
125
- # 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
126
121
  # matching header, will return an array of values in order that they appear
127
122
  # in the header ordered from top to bottom.
128
- #
123
+ #
129
124
  # Example:
130
- #
125
+ #
131
126
  # h = Header.new
132
127
  # h.fields = ['To: mikel@me.com', 'X-Mail-SPAM: 15', 'X-Mail-SPAM: 20']
133
128
  # h['To'] #=> 'mikel@me.com'
134
129
  # h['X-Mail-SPAM'] #=> ['15', '20']
135
130
  def [](name)
136
- name = dasherize(name)
137
- name.downcase!
138
- selected = select_field_for(name)
139
- case
140
- when selected.length > 1
141
- selected.map { |f| f }
142
- when !Utilities.blank?(selected)
143
- selected.first
144
- else
145
- nil
146
- end
131
+ fields.get_field(Utilities.dasherize(name))
147
132
  end
148
-
133
+
149
134
  # Sets the FIRST matching field in the header to passed value, or deletes
150
135
  # the FIRST field matched from the header if passed nil
151
- #
136
+ #
152
137
  # Example:
153
- #
138
+ #
154
139
  # h = Header.new
155
140
  # h.fields = ['To: mikel@me.com', 'X-Mail-SPAM: 15', 'X-Mail-SPAM: 20']
156
141
  # h['To'] = 'bob@you.com'
@@ -160,51 +145,39 @@ module Mail
160
145
  # h['X-Mail-SPAM'] = nil
161
146
  # h['X-Mail-SPAM'] # => nil
162
147
  def []=(name, value)
163
- name = dasherize(name)
164
- if name.include?(':')
148
+ name = name.to_s
149
+ if name.include?(Constants::COLON)
165
150
  raise ArgumentError, "Header names may not contain a colon: #{name.inspect}"
166
151
  end
167
- fn = name.downcase
168
- selected = select_field_for(fn)
169
-
170
- case
171
- # User wants to delete the field
172
- when !Utilities.blank?(selected) && value == nil
173
- fields.delete_if { |f| selected.include?(f) }
174
-
175
- # User wants to change the field
176
- when !Utilities.blank?(selected) && limited_field?(fn)
177
- selected.first.update(fn, value)
178
-
179
- # 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
180
158
  else
181
- # Need to insert in correct order for trace fields
182
- self.fields << Field.new(name.to_s, value, charset)
183
- end
184
- if dasherize(fn) == "content-type"
159
+ fields.add_field Field.new(name.to_s, value, charset)
160
+
185
161
  # Update charset if specified in Content-Type
186
- params = self[:content_type].parameters rescue nil
187
- @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
188
166
  end
189
167
  end
190
-
191
- def charset
192
- @charset
193
- end
194
-
168
+
195
169
  def charset=(val)
196
170
  params = self[:content_type].parameters rescue nil
197
171
  if params
198
- params[:charset] = val
172
+ if val
173
+ params[:charset] = val
174
+ else
175
+ params.delete(:charset)
176
+ end
199
177
  end
200
178
  @charset = val
201
179
  end
202
-
203
- LIMITED_FIELDS = %w[ date from sender reply-to to cc bcc
204
- message-id in-reply-to references subject
205
- return-path content-type mime-version
206
- content-transfer-encoding content-description
207
- content-id content-disposition content-location]
180
+
208
181
 
209
182
  def encoded
210
183
  buffer = String.new
@@ -218,61 +191,47 @@ module Mail
218
191
  def to_s
219
192
  encoded
220
193
  end
221
-
194
+
222
195
  def decoded
223
- 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.'
224
197
  end
225
198
 
226
199
  def field_summary
227
- fields.map { |f| "<#{f.name}: #{f.value}>" }.join(", ")
200
+ fields.summary
228
201
  end
229
202
 
230
203
  # Returns true if the header has a Message-ID defined (empty or not)
231
204
  def has_message_id?
232
- !fields.select { |f| f.responsible_for?('Message-ID') }.empty?
205
+ fields.has_field? 'Message-ID'
233
206
  end
234
207
 
235
208
  # Returns true if the header has a Content-ID defined (empty or not)
236
209
  def has_content_id?
237
- !fields.select { |f| f.responsible_for?('Content-ID') }.empty?
210
+ fields.has_field? 'Content-ID'
238
211
  end
239
212
 
240
213
  # Returns true if the header has a Date defined (empty or not)
241
214
  def has_date?
242
- !fields.select { |f| f.responsible_for?('Date') }.empty?
215
+ fields.has_field? 'Date'
243
216
  end
244
217
 
245
218
  # Returns true if the header has a MIME version defined (empty or not)
246
219
  def has_mime_version?
247
- !fields.select { |f| f.responsible_for?('Mime-Version') }.empty?
220
+ fields.has_field? 'Mime-Version'
248
221
  end
249
222
 
250
223
  private
251
-
252
- def raw_source=(val)
253
- @raw_source = ::Mail::Utilities.to_crlf(val).lstrip
254
- end
255
-
224
+
256
225
  # Splits an unfolded and line break cleaned header into individual field
257
226
  # strings.
258
227
  def split_header
259
- self.fields = raw_source.split(HEADER_SPLIT)
260
- end
261
-
262
- def select_field_for(name)
263
- fields.select { |f| f.responsible_for?(name) }
264
- end
265
-
266
- def limited_field?(name)
267
- LIMITED_FIELDS.include?(name.to_s.downcase)
228
+ self.fields = @raw_source.split(Constants::HEADER_SPLIT)
268
229
  end
269
230
 
270
- # Enumerable support; yield each field in order to the block if there is one,
271
- # or return an Enumerator for them if there isn't.
272
- def each( &block )
273
- return self.fields.each( &block ) if block
274
- self.fields.each
275
- end
276
231
 
232
+ # Enumerable support. Yield each field in order.
233
+ def each(&block)
234
+ fields.each(&block)
235
+ end
277
236
  end
278
237
  end
@@ -9,6 +9,10 @@ module Mail
9
9
  AttachmentFilenameMatcher.new(filename)
10
10
  end
11
11
 
12
+ def an_attachment_with_mime_type(filename)
13
+ AttachmentMimeTypeMatcher.new(filename)
14
+ end
15
+
12
16
  class AnyAttachmentMatcher
13
17
  def ===(other)
14
18
  other.attachment?
@@ -25,5 +29,16 @@ module Mail
25
29
  other.attachment? && other.filename == filename
26
30
  end
27
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
28
43
  end
29
44
  end