mail 2.5.2 → 2.6.4

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 (187) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.rdoc +124 -0
  3. data/CONTRIBUTING.md +21 -6
  4. data/Dependencies.txt +0 -1
  5. data/Gemfile +7 -22
  6. data/MIT-LICENSE +20 -0
  7. data/README.md +86 -31
  8. data/Rakefile +11 -22
  9. data/lib/mail/attachments_list.rb +4 -2
  10. data/lib/mail/body.rb +36 -11
  11. data/lib/mail/check_delivery_params.rb +13 -22
  12. data/lib/mail/configuration.rb +1 -0
  13. data/lib/mail/{patterns.rb → constants.rb} +29 -6
  14. data/lib/mail/core_extensions/smtp.rb +13 -13
  15. data/lib/mail/core_extensions/string/access.rb +1 -0
  16. data/lib/mail/core_extensions/string/multibyte.rb +1 -0
  17. data/lib/mail/core_extensions/string.rb +5 -17
  18. data/lib/mail/elements/address.rb +86 -145
  19. data/lib/mail/elements/address_list.rb +10 -42
  20. data/lib/mail/elements/content_disposition_element.rb +4 -7
  21. data/lib/mail/elements/content_location_element.rb +8 -11
  22. data/lib/mail/elements/content_transfer_encoding_element.rb +4 -10
  23. data/lib/mail/elements/content_type_element.rb +5 -8
  24. data/lib/mail/elements/date_time_element.rb +4 -7
  25. data/lib/mail/elements/envelope_from_element.rb +19 -13
  26. data/lib/mail/elements/message_ids_element.rb +8 -11
  27. data/lib/mail/elements/mime_version_element.rb +4 -7
  28. data/lib/mail/elements/phrase_list.rb +3 -7
  29. data/lib/mail/elements/received_element.rb +4 -7
  30. data/lib/mail/elements.rb +1 -0
  31. data/lib/mail/encodings/7bit.rb +1 -0
  32. data/lib/mail/encodings/8bit.rb +8 -2
  33. data/lib/mail/encodings/base64.rb +7 -1
  34. data/lib/mail/encodings/binary.rb +1 -0
  35. data/lib/mail/encodings/quoted_printable.rb +11 -4
  36. data/lib/mail/encodings/transfer_encoding.rb +27 -17
  37. data/lib/mail/encodings/unix_to_unix.rb +18 -0
  38. data/lib/mail/encodings.rb +54 -39
  39. data/lib/mail/envelope.rb +1 -5
  40. data/lib/mail/field.rb +81 -31
  41. data/lib/mail/field_list.rb +19 -18
  42. data/lib/mail/fields/bcc_field.rb +17 -5
  43. data/lib/mail/fields/cc_field.rb +3 -3
  44. data/lib/mail/fields/comments_field.rb +2 -1
  45. data/lib/mail/fields/common/address_container.rb +1 -0
  46. data/lib/mail/fields/common/common_address.rb +34 -28
  47. data/lib/mail/fields/common/common_date.rb +2 -8
  48. data/lib/mail/fields/common/common_field.rb +9 -5
  49. data/lib/mail/fields/common/common_message_id.rb +3 -2
  50. data/lib/mail/fields/common/parameter_hash.rb +4 -3
  51. data/lib/mail/fields/content_description_field.rb +1 -0
  52. data/lib/mail/fields/content_disposition_field.rb +13 -12
  53. data/lib/mail/fields/content_id_field.rb +4 -4
  54. data/lib/mail/fields/content_location_field.rb +2 -1
  55. data/lib/mail/fields/content_transfer_encoding_field.rb +4 -9
  56. data/lib/mail/fields/content_type_field.rb +4 -3
  57. data/lib/mail/fields/date_field.rb +16 -15
  58. data/lib/mail/fields/from_field.rb +3 -3
  59. data/lib/mail/fields/in_reply_to_field.rb +3 -1
  60. data/lib/mail/fields/keywords_field.rb +2 -2
  61. data/lib/mail/fields/message_id_field.rb +4 -4
  62. data/lib/mail/fields/mime_version_field.rb +3 -2
  63. data/lib/mail/fields/optional_field.rb +1 -0
  64. data/lib/mail/fields/received_field.rb +4 -3
  65. data/lib/mail/fields/references_field.rb +3 -1
  66. data/lib/mail/fields/reply_to_field.rb +3 -3
  67. data/lib/mail/fields/resent_bcc_field.rb +3 -3
  68. data/lib/mail/fields/resent_cc_field.rb +3 -3
  69. data/lib/mail/fields/resent_date_field.rb +2 -1
  70. data/lib/mail/fields/resent_from_field.rb +3 -3
  71. data/lib/mail/fields/resent_message_id_field.rb +1 -0
  72. data/lib/mail/fields/resent_sender_field.rb +4 -4
  73. data/lib/mail/fields/resent_to_field.rb +3 -3
  74. data/lib/mail/fields/return_path_field.rb +1 -1
  75. data/lib/mail/fields/sender_field.rb +9 -9
  76. data/lib/mail/fields/structured_field.rb +1 -0
  77. data/lib/mail/fields/subject_field.rb +1 -0
  78. data/lib/mail/fields/to_field.rb +3 -3
  79. data/lib/mail/fields/unstructured_field.rb +15 -8
  80. data/lib/mail/fields.rb +1 -0
  81. data/lib/mail/header.rb +21 -29
  82. data/lib/mail/indifferent_hash.rb +1 -0
  83. data/lib/mail/mail.rb +16 -1
  84. data/lib/mail/matchers/attachment_matchers.rb +29 -0
  85. data/lib/mail/matchers/has_sent_mail.rb +61 -4
  86. data/lib/mail/message.rb +175 -72
  87. data/lib/mail/multibyte/chars.rb +3 -2
  88. data/lib/mail/multibyte/exceptions.rb +1 -0
  89. data/lib/mail/multibyte/unicode.rb +22 -15
  90. data/lib/mail/multibyte/utils.rb +1 -0
  91. data/lib/mail/multibyte.rb +1 -0
  92. data/lib/mail/network/delivery_methods/exim.rb +3 -7
  93. data/lib/mail/network/delivery_methods/file_delivery.rb +2 -1
  94. data/lib/mail/network/delivery_methods/sendmail.rb +33 -10
  95. data/lib/mail/network/delivery_methods/smtp.rb +32 -34
  96. data/lib/mail/network/delivery_methods/smtp_connection.rb +7 -6
  97. data/lib/mail/network/delivery_methods/test_mailer.rb +3 -2
  98. data/lib/mail/network/retriever_methods/base.rb +1 -0
  99. data/lib/mail/network/retriever_methods/imap.rb +19 -13
  100. data/lib/mail/network/retriever_methods/pop3.rb +1 -0
  101. data/lib/mail/network/retriever_methods/test_retriever.rb +1 -0
  102. data/lib/mail/network.rb +1 -0
  103. data/lib/mail/parsers/address_lists_parser.rb +133 -0
  104. data/lib/mail/parsers/content_disposition_parser.rb +68 -0
  105. data/lib/mail/parsers/content_location_parser.rb +36 -0
  106. data/lib/mail/parsers/content_transfer_encoding_parser.rb +34 -0
  107. data/lib/mail/parsers/content_type_parser.rb +67 -0
  108. data/lib/mail/parsers/date_time_parser.rb +39 -0
  109. data/lib/mail/parsers/envelope_from_parser.rb +46 -0
  110. data/lib/mail/parsers/message_ids_parser.rb +40 -0
  111. data/lib/mail/parsers/mime_version_parser.rb +42 -0
  112. data/lib/mail/parsers/phrase_lists_parser.rb +36 -0
  113. data/lib/mail/parsers/ragel/common.rl +185 -0
  114. data/lib/mail/parsers/ragel/date_time.rl +30 -0
  115. data/lib/mail/parsers/ragel/parser_info.rb +61 -0
  116. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +14864 -0
  117. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +37 -0
  118. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +751 -0
  119. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +37 -0
  120. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +614 -0
  121. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +37 -0
  122. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +447 -0
  123. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +37 -0
  124. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +825 -0
  125. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +37 -0
  126. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +817 -0
  127. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +37 -0
  128. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +2149 -0
  129. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +37 -0
  130. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +1570 -0
  131. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +37 -0
  132. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +440 -0
  133. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +37 -0
  134. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +564 -0
  135. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +37 -0
  136. data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +51 -0
  137. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +5144 -0
  138. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +37 -0
  139. data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +37 -0
  140. data/lib/mail/parsers/ragel/ruby.rb +40 -0
  141. data/lib/mail/parsers/ragel.rb +18 -0
  142. data/lib/mail/parsers/received_parser.rb +49 -0
  143. data/lib/mail/parsers.rb +27 -0
  144. data/lib/mail/part.rb +11 -4
  145. data/lib/mail/parts_list.rb +30 -10
  146. data/lib/mail/utilities.rb +127 -63
  147. data/lib/mail/values/unicode_tables.dat +0 -0
  148. data/lib/mail/version.rb +8 -15
  149. data/lib/mail/version_specific/ruby_1_8.rb +18 -7
  150. data/lib/mail/version_specific/ruby_1_9.rb +136 -32
  151. data/lib/mail.rb +12 -6
  152. metadata +115 -77
  153. data/lib/VERSION +0 -4
  154. data/lib/load_parsers.rb +0 -41
  155. data/lib/mail/core_extensions/nil.rb +0 -19
  156. data/lib/mail/core_extensions/object.rb +0 -13
  157. data/lib/mail/core_extensions/shell_escape.rb +0 -56
  158. data/lib/mail/parsers/address_lists.rb +0 -64
  159. data/lib/mail/parsers/address_lists.treetop +0 -19
  160. data/lib/mail/parsers/content_disposition.rb +0 -535
  161. data/lib/mail/parsers/content_disposition.treetop +0 -46
  162. data/lib/mail/parsers/content_location.rb +0 -139
  163. data/lib/mail/parsers/content_location.treetop +0 -20
  164. data/lib/mail/parsers/content_transfer_encoding.rb +0 -162
  165. data/lib/mail/parsers/content_transfer_encoding.treetop +0 -20
  166. data/lib/mail/parsers/content_type.rb +0 -967
  167. data/lib/mail/parsers/content_type.treetop +0 -68
  168. data/lib/mail/parsers/date_time.rb +0 -114
  169. data/lib/mail/parsers/date_time.treetop +0 -11
  170. data/lib/mail/parsers/envelope_from.rb +0 -194
  171. data/lib/mail/parsers/envelope_from.treetop +0 -32
  172. data/lib/mail/parsers/message_ids.rb +0 -45
  173. data/lib/mail/parsers/message_ids.treetop +0 -15
  174. data/lib/mail/parsers/mime_version.rb +0 -144
  175. data/lib/mail/parsers/mime_version.treetop +0 -19
  176. data/lib/mail/parsers/phrase_lists.rb +0 -45
  177. data/lib/mail/parsers/phrase_lists.treetop +0 -15
  178. data/lib/mail/parsers/received.rb +0 -71
  179. data/lib/mail/parsers/received.treetop +0 -11
  180. data/lib/mail/parsers/rfc2045.rb +0 -464
  181. data/lib/mail/parsers/rfc2045.treetop +0 -36
  182. data/lib/mail/parsers/rfc2822.rb +0 -5397
  183. data/lib/mail/parsers/rfc2822.treetop +0 -410
  184. data/lib/mail/parsers/rfc2822_obsolete.rb +0 -3768
  185. data/lib/mail/parsers/rfc2822_obsolete.treetop +0 -241
  186. data/lib/tasks/corpus.rake +0 -125
  187. data/lib/tasks/treetop.rake +0 -10
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  #
3
4
  # 4.4.3. REPLY-TO / RESENT-REPLY-TO
4
5
  #
@@ -41,7 +42,6 @@ module Mail
41
42
  value = nil if value == '<>'
42
43
  self.charset = charset
43
44
  super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
44
- self.parse
45
45
  self
46
46
  end
47
47
 
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  #
3
4
  # = Sender Field
4
5
  #
@@ -6,7 +7,7 @@
6
7
  # field in the email.
7
8
  #
8
9
  # Sending sender to a mail message will instantiate a Mail::Field object that
9
- # has a SenderField as it's field type. This includes all Mail::CommonAddress
10
+ # has a SenderField as its field type. This includes all Mail::CommonAddress
10
11
  # module instance metods.
11
12
  #
12
13
  # Only one Sender field can appear in a header, though it can have multiple
@@ -15,16 +16,16 @@
15
16
  # == Examples:
16
17
  #
17
18
  # mail = Mail.new
18
- # mail.sender = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
19
- # mail.sender #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
19
+ # mail.sender = 'Mikel Lindsaar <mikel@test.lindsaar.net>'
20
+ # mail.sender #=> 'mikel@test.lindsaar.net'
20
21
  # mail[:sender] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::SenderField:0x180e1c4
21
22
  # mail['sender'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::SenderField:0x180e1c4
22
23
  # mail['Sender'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::SenderField:0x180e1c4
23
24
  #
24
- # mail[:sender].encoded #=> 'Sender: Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net\r\n'
25
- # mail[:sender].decoded #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
26
- # mail[:sender].addresses #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
27
- # mail[:sender].formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
25
+ # mail[:sender].encoded #=> "Sender: Mikel Lindsaar <mikel@test.lindsaar.net>\r\n"
26
+ # mail[:sender].decoded #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>'
27
+ # mail[:sender].addresses #=> ['mikel@test.lindsaar.net']
28
+ # mail[:sender].formatted #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>']
28
29
  #
29
30
  require 'mail/fields/common/common_address'
30
31
 
@@ -39,7 +40,6 @@ module Mail
39
40
  def initialize(value = nil, charset = 'utf-8')
40
41
  self.charset = charset
41
42
  super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
42
- self.parse
43
43
  self
44
44
  end
45
45
 
@@ -48,7 +48,7 @@ module Mail
48
48
  end
49
49
 
50
50
  def address
51
- tree.addresses.first
51
+ address_list.addresses.first
52
52
  end
53
53
 
54
54
  def encoded
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  require 'mail/fields/common/common_field'
3
4
 
4
5
  module Mail
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  #
3
4
  # subject = "Subject:" unstructured CRLF
4
5
  module Mail
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  #
3
4
  # = To Field
4
5
  #
@@ -6,7 +7,7 @@
6
7
  # field in the email.
7
8
  #
8
9
  # Sending to to a mail message will instantiate a Mail::Field object that
9
- # has a ToField as it's field type. This includes all Mail::CommonAddress
10
+ # has a ToField as its field type. This includes all Mail::CommonAddress
10
11
  # module instance metods.
11
12
  #
12
13
  # Only one To field can appear in a header, though it can have multiple
@@ -16,7 +17,7 @@
16
17
  #
17
18
  # mail = Mail.new
18
19
  # mail.to = 'Mikel Lindsaar <mikel@test.lindsaar.net>, ada@test.lindsaar.net'
19
- # mail.to #=> ['Mikel Lindsaar <mikel@test.lindsaar.net>', 'ada@test.lindsaar.net']
20
+ # mail.to #=> ['mikel@test.lindsaar.net', 'ada@test.lindsaar.net']
20
21
  # mail[:to] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ToField:0x180e1c4
21
22
  # mail['to'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ToField:0x180e1c4
22
23
  # mail['To'] #=> '#<Mail::Field:0x180e5e8 @field=#<Mail::ToField:0x180e1c4
@@ -39,7 +40,6 @@ module Mail
39
40
  def initialize(value = nil, charset = 'utf-8')
40
41
  self.charset = charset
41
42
  super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
42
- self.parse
43
43
  self
44
44
  end
45
45
 
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  require 'mail/fields/common/common_field'
3
4
 
4
5
  module Mail
@@ -70,7 +71,7 @@ module Mail
70
71
  end
71
72
 
72
73
  def do_decode
73
- value.blank? ? nil : Encodings.decode_encode(value, :decode)
74
+ Utilities.blank?(value) ? nil : Encodings.decode_encode(value, :decode)
74
75
  end
75
76
 
76
77
  # 2.2.3. Long Header Fields
@@ -127,7 +128,7 @@ module Mail
127
128
  if first
128
129
  first = !first
129
130
  else
130
- word = " " << word
131
+ word = " #{word}"
131
132
  end
132
133
  if word.not_ascii_only?
133
134
  word
@@ -143,10 +144,11 @@ module Mail
143
144
  while !words.empty?
144
145
  limit = 78 - prepend
145
146
  limit = limit - 7 - encoding.length if should_encode
146
- line = ""
147
+ line = String.new
148
+ first_word = true
147
149
  while !words.empty?
148
150
  break unless word = words.first.dup
149
- word.encode!(charset) if defined?(Encoding) && charset
151
+ word.encode!(charset) if charset && word.respond_to?(:encode!)
150
152
  word = encode(word) if should_encode
151
153
  word = encode_crlf(word)
152
154
  # Skip to next line if we're going to go past the limit
@@ -158,7 +160,12 @@ module Mail
158
160
  # Remove the word from the queue ...
159
161
  words.shift
160
162
  # Add word separator
161
- line << " " unless (line.empty? || should_encode)
163
+ if first_word
164
+ first_word = false
165
+ else
166
+ line << " " if !should_encode
167
+ end
168
+
162
169
  # ... add it in encoded form to the current line
163
170
  line << word
164
171
  end
@@ -172,7 +179,7 @@ module Mail
172
179
  end
173
180
 
174
181
  def encode(value)
175
- value = [value].pack("M").gsub("=\n", '')
182
+ value = [value].pack(CAPITAL_M).gsub(EQUAL_LF, EMPTY)
176
183
  value.gsub!(/"/, '=22')
177
184
  value.gsub!(/\(/, '=28')
178
185
  value.gsub!(/\)/, '=29')
@@ -183,8 +190,8 @@ module Mail
183
190
  end
184
191
 
185
192
  def encode_crlf(value)
186
- value.gsub!("\r", '=0D')
187
- value.gsub!("\n", '=0A')
193
+ value.gsub!(CR, CR_ENCODED)
194
+ value.gsub!(LF, LF_ENCODED)
188
195
  value
189
196
  end
190
197
 
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,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  module Mail
3
4
 
4
5
  # Provides access to a header object.
@@ -17,7 +18,7 @@ module Mail
17
18
  # 2.2.3. All field bodies MUST conform to the syntax described in
18
19
  # sections 3 and 4 of this standard.
19
20
  class Header
20
- include Patterns
21
+ include Constants
21
22
  include Utilities
22
23
  include Enumerable
23
24
 
@@ -48,11 +49,15 @@ module Mail
48
49
  # these cases, please make a patch and send it in, or at the least, send
49
50
  # me the example so we can fix it.
50
51
  def initialize(header_text = nil, charset = nil)
51
- @errors = []
52
52
  @charset = charset
53
- self.raw_source = header_text.to_crlf
53
+ self.raw_source = ::Mail::Utilities.to_crlf(header_text).lstrip
54
54
  split_header if header_text
55
55
  end
56
+
57
+ def initialize_copy(original)
58
+ super
59
+ @fields = @fields.dup
60
+ end
56
61
 
57
62
  # The preserved raw source of the header as you passed it in, untouched
58
63
  # for your Regexing glory.
@@ -91,7 +96,6 @@ module Mail
91
96
  unfolded_fields[0..(self.class.maximum_amount-1)].each do |field|
92
97
 
93
98
  field = Field.new(field, nil, charset)
94
- field.errors.each { |error| self.errors << error }
95
99
  if limited_field?(field.name) && (selected = select_field_for(field.name)) && selected.any?
96
100
  selected.first.update(field.name, field.value)
97
101
  else
@@ -102,7 +106,7 @@ module Mail
102
106
  end
103
107
 
104
108
  def errors
105
- @errors
109
+ @fields.map(&:errors).flatten(1)
106
110
  end
107
111
 
108
112
  # 3.6. Field definitions
@@ -127,12 +131,13 @@ module Mail
127
131
  # h['To'] #=> 'mikel@me.com'
128
132
  # h['X-Mail-SPAM'] #=> ['15', '20']
129
133
  def [](name)
130
- name = dasherize(name).downcase
134
+ name = dasherize(name)
135
+ name.downcase!
131
136
  selected = select_field_for(name)
132
137
  case
133
138
  when selected.length > 1
134
139
  selected.map { |f| f }
135
- when !selected.blank?
140
+ when !Utilities.blank?(selected)
136
141
  selected.first
137
142
  else
138
143
  nil
@@ -154,16 +159,19 @@ module Mail
154
159
  # h['X-Mail-SPAM'] # => nil
155
160
  def []=(name, value)
156
161
  name = dasherize(name)
162
+ if name.include?(':')
163
+ raise ArgumentError, "Header names may not contain a colon: #{name.inspect}"
164
+ end
157
165
  fn = name.downcase
158
166
  selected = select_field_for(fn)
159
167
 
160
168
  case
161
169
  # User wants to delete the field
162
- when !selected.blank? && value == nil
170
+ when !Utilities.blank?(selected) && value == nil
163
171
  fields.delete_if { |f| selected.include?(f) }
164
172
 
165
173
  # User wants to change the field
166
- when !selected.blank? && limited_field?(fn)
174
+ when !Utilities.blank?(selected) && limited_field?(fn)
167
175
  selected.first.update(fn, value)
168
176
 
169
177
  # User wants to create the field
@@ -174,7 +182,7 @@ module Mail
174
182
  if dasherize(fn) == "content-type"
175
183
  # Update charset if specified in Content-Type
176
184
  params = self[:content_type].parameters rescue nil
177
- @charset = params && params[:charset]
185
+ @charset = params[:charset] if params && params[:charset]
178
186
  end
179
187
  end
180
188
 
@@ -197,7 +205,8 @@ module Mail
197
205
  content-id content-disposition content-location]
198
206
 
199
207
  def encoded
200
- buffer = ''
208
+ buffer = String.new
209
+ buffer.force_encoding('us-ascii') if buffer.respond_to?(:force_encoding)
201
210
  fields.each do |field|
202
211
  buffer << field.encoded
203
212
  end
@@ -242,27 +251,10 @@ module Mail
242
251
  @raw_source = val
243
252
  end
244
253
 
245
- # 2.2.3. Long Header Fields
246
- #
247
- # The process of moving from this folded multiple-line representation
248
- # of a header field to its single line representation is called
249
- # "unfolding". Unfolding is accomplished by simply removing any CRLF
250
- # that is immediately followed by WSP. Each header field should be
251
- # treated in its unfolded form for further syntactic and semantic
252
- # evaluation.
253
- def unfold(string)
254
- string.gsub(/#{CRLF}#{WSP}+/, ' ').gsub(/#{WSP}+/, ' ')
255
- end
256
-
257
- # Returns the header with all the folds removed
258
- def unfolded_header
259
- @unfolded_header ||= unfold(raw_source)
260
- end
261
-
262
254
  # Splits an unfolded and line break cleaned header into individual field
263
255
  # strings.
264
256
  def split_header
265
- self.fields = unfolded_header.split(CRLF)
257
+ self.fields = raw_source.split(HEADER_SPLIT)
266
258
  end
267
259
 
268
260
  def select_field_for(name)
@@ -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.
@@ -206,6 +207,12 @@ module Mail
206
207
  end
207
208
  end
208
209
 
210
+ # Unregister the given observer, allowing mail to resume operations
211
+ # without it.
212
+ def self.unregister_observer(observer)
213
+ @@delivery_notification_observers.delete(observer)
214
+ end
215
+
209
216
  # You can register an object to be given every mail object that will be sent,
210
217
  # before it is sent. So if you want to add special headers or modify any
211
218
  # email that gets sent through the Mail library, you can do so.
@@ -219,6 +226,12 @@ module Mail
219
226
  end
220
227
  end
221
228
 
229
+ # Unregister the given interceptor, allowing mail to resume operations
230
+ # without it.
231
+ def self.unregister_interceptor(interceptor)
232
+ @@delivery_interceptors.delete(interceptor)
233
+ end
234
+
222
235
  def self.inform_observers(mail)
223
236
  @@delivery_notification_observers.each do |observer|
224
237
  observer.delivered_email(mail)
@@ -233,9 +246,11 @@ module Mail
233
246
 
234
247
  protected
235
248
 
249
+ RANDOM_TAG='%x%x_%x%x%d%x'
250
+
236
251
  def self.random_tag
237
252
  t = Time.now
238
- sprintf('%x%x_%x%x%d%x',
253
+ sprintf(RANDOM_TAG,
239
254
  t.to_i, t.tv_usec,
240
255
  $$, Thread.current.object_id.abs, self.uniq, rand(255))
241
256
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+ module Mail
3
+ module Matchers
4
+ def any_attachment
5
+ AnyAttachmentMatcher.new
6
+ end
7
+
8
+ def an_attachment_with_filename(filename)
9
+ AttachmentFilenameMatcher.new(filename)
10
+ end
11
+
12
+ class AnyAttachmentMatcher
13
+ def ===(other)
14
+ other.attachment?
15
+ end
16
+ end
17
+
18
+ class AttachmentFilenameMatcher
19
+ attr_reader :filename
20
+ def initialize(filename)
21
+ @filename = filename
22
+ end
23
+
24
+ def ===(other)
25
+ other.attachment? && other.filename == filename
26
+ end
27
+ end
28
+ end
29
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Mail
2
3
  module Matchers
3
4
  def have_sent_email
@@ -29,6 +30,39 @@ module Mail
29
30
  self
30
31
  end
31
32
 
33
+ def cc(recipient_or_list)
34
+ @copy_recipients ||= []
35
+
36
+ if recipient_or_list.kind_of?(Array)
37
+ @copy_recipients += recipient_or_list
38
+ else
39
+ @copy_recipients << recipient_or_list
40
+ end
41
+ self
42
+ end
43
+
44
+ def bcc(recipient_or_list)
45
+ @blind_copy_recipients ||= []
46
+ @blind_copy_recipients.concat(Array(recipient_or_list))
47
+ self
48
+ end
49
+
50
+ def with_attachments(attachments)
51
+ @attachments ||= []
52
+ @attachments.concat(Array(attachments))
53
+ self
54
+ end
55
+
56
+ def with_no_attachments
57
+ @having_attachments = false
58
+ self
59
+ end
60
+
61
+ def with_any_attachments
62
+ @having_attachments = true
63
+ self
64
+ end
65
+
32
66
  def with_subject(subject)
33
67
  @subject = subject
34
68
  self
@@ -61,7 +95,7 @@ module Mail
61
95
  result
62
96
  end
63
97
 
64
- def negative_failure_message
98
+ def failure_message_when_negated
65
99
  result = "Expected no email to be sent "
66
100
  result += explain_expectations
67
101
  result += dump_deliveries
@@ -69,11 +103,13 @@ module Mail
69
103
  end
70
104
 
71
105
  protected
72
-
106
+
73
107
  def filter_matched_deliveries(deliveries)
74
108
  candidate_deliveries = deliveries
75
-
76
- %w(sender recipients subject subject_matcher body body_matcher).each do |modifier_name|
109
+ modifiers =
110
+ %w(sender recipients copy_recipients blind_copy_recipients subject
111
+ subject_matcher body body_matcher having_attachments attachments)
112
+ modifiers.each do |modifier_name|
77
113
  next unless instance_variable_defined?("@#{modifier_name}")
78
114
  candidate_deliveries = candidate_deliveries.select{|matching_delivery| self.send("matches_on_#{modifier_name}?", matching_delivery)}
79
115
  end
@@ -89,6 +125,14 @@ module Mail
89
125
  @recipients.all? {|recipient| delivery.to.include?(recipient) }
90
126
  end
91
127
 
128
+ def matches_on_copy_recipients?(delivery)
129
+ @copy_recipients.all? {|recipient| delivery.cc.include?(recipient) }
130
+ end
131
+
132
+ def matches_on_blind_copy_recipients?(delivery)
133
+ @blind_copy_recipients.all? {|recipient| delivery.bcc.include?(recipient) }
134
+ end
135
+
92
136
  def matches_on_subject?(delivery)
93
137
  delivery.subject == @subject
94
138
  end
@@ -97,6 +141,17 @@ module Mail
97
141
  @subject_matcher.match delivery.subject
98
142
  end
99
143
 
144
+ def matches_on_having_attachments?(delivery)
145
+ @having_attachments && delivery.attachments.any? ||
146
+ (!@having_attachments && delivery.attachments.none?)
147
+ end
148
+
149
+ def matches_on_attachments?(delivery)
150
+ @attachments.each_with_index.inject( true ) do |sent_attachments, (attachment, index)|
151
+ sent_attachments &&= (attachment === delivery.attachments[index])
152
+ end
153
+ end
154
+
100
155
  def matches_on_body?(delivery)
101
156
  delivery.body == @body
102
157
  end
@@ -109,6 +164,8 @@ module Mail
109
164
  result = ''
110
165
  result += "from #{@sender} " if instance_variable_defined?('@sender')
111
166
  result += "to #{@recipients.inspect} " if instance_variable_defined?('@recipients')
167
+ result += "cc #{@copy_recipients.inspect} " if instance_variable_defined?('@copy_recipients')
168
+ result += "bcc #{@blind_copy_recipients.inspect} " if instance_variable_defined?('@blind_copy_recipients')
112
169
  result += "with subject \"#{@subject}\" " if instance_variable_defined?('@subject')
113
170
  result += "with subject matching \"#{@subject_matcher}\" " if instance_variable_defined?('@subject_matcher')
114
171
  result += "with body \"#{@body}\" " if instance_variable_defined?('@body')