mail 2.6.1 → 2.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (179) hide show
  1. checksums.yaml +5 -5
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +92 -80
  4. data/lib/mail/attachments_list.rb +11 -5
  5. data/lib/mail/body.rb +81 -44
  6. data/lib/mail/check_delivery_params.rb +50 -10
  7. data/lib/mail/configuration.rb +3 -0
  8. data/lib/mail/{patterns.rb → constants.rb} +26 -6
  9. data/lib/mail/core_extensions/smtp.rb +20 -16
  10. data/lib/mail/core_extensions/string.rb +1 -27
  11. data/lib/mail/elements/address.rb +81 -93
  12. data/lib/mail/elements/address_list.rb +12 -29
  13. data/lib/mail/elements/content_disposition_element.rb +9 -15
  14. data/lib/mail/elements/content_location_element.rb +8 -12
  15. data/lib/mail/elements/content_transfer_encoding_element.rb +6 -10
  16. data/lib/mail/elements/content_type_element.rb +9 -19
  17. data/lib/mail/elements/date_time_element.rb +7 -14
  18. data/lib/mail/elements/envelope_from_element.rb +15 -21
  19. data/lib/mail/elements/message_ids_element.rb +12 -14
  20. data/lib/mail/elements/mime_version_element.rb +7 -14
  21. data/lib/mail/elements/phrase_list.rb +7 -9
  22. data/lib/mail/elements/received_element.rb +10 -15
  23. data/lib/mail/elements.rb +1 -0
  24. data/lib/mail/encodings/7bit.rb +6 -15
  25. data/lib/mail/encodings/8bit.rb +5 -18
  26. data/lib/mail/encodings/base64.rb +15 -10
  27. data/lib/mail/encodings/binary.rb +4 -22
  28. data/lib/mail/encodings/identity.rb +24 -0
  29. data/lib/mail/encodings/quoted_printable.rb +13 -7
  30. data/lib/mail/encodings/transfer_encoding.rb +47 -28
  31. data/lib/mail/encodings/unix_to_unix.rb +20 -0
  32. data/lib/mail/encodings.rb +121 -82
  33. data/lib/mail/envelope.rb +2 -1
  34. data/lib/mail/field.rb +114 -62
  35. data/lib/mail/field_list.rb +2 -1
  36. data/lib/mail/fields/bcc_field.rb +17 -5
  37. data/lib/mail/fields/cc_field.rb +2 -2
  38. data/lib/mail/fields/comments_field.rb +2 -1
  39. data/lib/mail/fields/common/address_container.rb +3 -2
  40. data/lib/mail/fields/common/common_address.rb +40 -14
  41. data/lib/mail/fields/common/common_date.rb +2 -1
  42. data/lib/mail/fields/common/common_field.rb +6 -11
  43. data/lib/mail/fields/common/common_message_id.rb +3 -2
  44. data/lib/mail/fields/common/parameter_hash.rb +5 -4
  45. data/lib/mail/fields/content_description_field.rb +2 -1
  46. data/lib/mail/fields/content_disposition_field.rb +14 -13
  47. data/lib/mail/fields/content_id_field.rb +5 -4
  48. data/lib/mail/fields/content_location_field.rb +3 -2
  49. data/lib/mail/fields/content_transfer_encoding_field.rb +3 -2
  50. data/lib/mail/fields/content_type_field.rb +7 -11
  51. data/lib/mail/fields/date_field.rb +4 -4
  52. data/lib/mail/fields/from_field.rb +2 -2
  53. data/lib/mail/fields/in_reply_to_field.rb +2 -1
  54. data/lib/mail/fields/keywords_field.rb +3 -3
  55. data/lib/mail/fields/message_id_field.rb +3 -2
  56. data/lib/mail/fields/mime_version_field.rb +4 -3
  57. data/lib/mail/fields/optional_field.rb +5 -1
  58. data/lib/mail/fields/received_field.rb +5 -4
  59. data/lib/mail/fields/references_field.rb +2 -1
  60. data/lib/mail/fields/reply_to_field.rb +2 -2
  61. data/lib/mail/fields/resent_bcc_field.rb +2 -2
  62. data/lib/mail/fields/resent_cc_field.rb +2 -2
  63. data/lib/mail/fields/resent_date_field.rb +2 -2
  64. data/lib/mail/fields/resent_from_field.rb +2 -2
  65. data/lib/mail/fields/resent_message_id_field.rb +2 -1
  66. data/lib/mail/fields/resent_sender_field.rb +2 -2
  67. data/lib/mail/fields/resent_to_field.rb +2 -2
  68. data/lib/mail/fields/return_path_field.rb +2 -2
  69. data/lib/mail/fields/sender_field.rb +2 -2
  70. data/lib/mail/fields/structured_field.rb +1 -0
  71. data/lib/mail/fields/subject_field.rb +2 -1
  72. data/lib/mail/fields/to_field.rb +2 -2
  73. data/lib/mail/fields/unstructured_field.rb +28 -10
  74. data/lib/mail/fields.rb +1 -0
  75. data/lib/mail/header.rb +18 -14
  76. data/lib/mail/indifferent_hash.rb +1 -0
  77. data/lib/mail/mail.rb +6 -11
  78. data/lib/mail/matchers/attachment_matchers.rb +29 -0
  79. data/lib/mail/matchers/has_sent_mail.rb +53 -9
  80. data/lib/mail/message.rb +99 -89
  81. data/lib/mail/multibyte/chars.rb +32 -30
  82. data/lib/mail/multibyte/unicode.rb +31 -26
  83. data/lib/mail/multibyte/utils.rb +1 -0
  84. data/lib/mail/multibyte.rb +65 -15
  85. data/lib/mail/network/delivery_methods/exim.rb +7 -10
  86. data/lib/mail/network/delivery_methods/file_delivery.rb +5 -8
  87. data/lib/mail/network/delivery_methods/logger_delivery.rb +37 -0
  88. data/lib/mail/network/delivery_methods/sendmail.rb +17 -11
  89. data/lib/mail/network/delivery_methods/smtp.rb +60 -53
  90. data/lib/mail/network/delivery_methods/smtp_connection.rb +11 -6
  91. data/lib/mail/network/delivery_methods/test_mailer.rb +6 -8
  92. data/lib/mail/network/retriever_methods/base.rb +1 -0
  93. data/lib/mail/network/retriever_methods/imap.rb +19 -5
  94. data/lib/mail/network/retriever_methods/pop3.rb +4 -1
  95. data/lib/mail/network/retriever_methods/test_retriever.rb +2 -1
  96. data/lib/mail/network.rb +2 -0
  97. data/lib/mail/parser_tools.rb +15 -0
  98. data/lib/mail/parsers/address_lists_parser.rb +33208 -104
  99. data/lib/mail/parsers/address_lists_parser.rl +172 -0
  100. data/lib/mail/parsers/content_disposition_parser.rb +877 -49
  101. data/lib/mail/parsers/content_disposition_parser.rl +82 -0
  102. data/lib/mail/parsers/content_location_parser.rb +804 -23
  103. data/lib/mail/parsers/content_location_parser.rl +71 -0
  104. data/lib/mail/parsers/content_transfer_encoding_parser.rb +502 -19
  105. data/lib/mail/parsers/content_transfer_encoding_parser.rl +64 -0
  106. data/lib/mail/parsers/content_type_parser.rb +1024 -46
  107. data/lib/mail/parsers/content_type_parser.rl +83 -0
  108. data/lib/mail/parsers/date_time_parser.rb +872 -23
  109. data/lib/mail/parsers/date_time_parser.rl +62 -0
  110. data/lib/mail/parsers/envelope_from_parser.rb +3570 -34
  111. data/lib/mail/parsers/envelope_from_parser.rl +82 -0
  112. data/lib/mail/parsers/message_ids_parser.rb +2840 -25
  113. data/lib/mail/parsers/message_ids_parser.rl +82 -0
  114. data/lib/mail/parsers/mime_version_parser.rb +492 -26
  115. data/lib/mail/parsers/mime_version_parser.rl +61 -0
  116. data/lib/mail/parsers/phrase_lists_parser.rb +862 -17
  117. data/lib/mail/parsers/phrase_lists_parser.rl +83 -0
  118. data/lib/mail/parsers/received_parser.rb +8765 -36
  119. data/lib/mail/parsers/received_parser.rl +84 -0
  120. data/lib/mail/parsers/rfc2045_content_transfer_encoding.rl +13 -0
  121. data/lib/mail/parsers/rfc2045_content_type.rl +25 -0
  122. data/lib/mail/parsers/rfc2045_mime.rl +16 -0
  123. data/lib/mail/parsers/rfc2183_content_disposition.rl +15 -0
  124. data/lib/mail/parsers/rfc3629_utf8.rl +19 -0
  125. data/lib/mail/parsers/rfc5234_abnf_core_rules.rl +22 -0
  126. data/lib/mail/parsers/rfc5322.rl +59 -0
  127. data/lib/mail/parsers/rfc5322_address.rl +72 -0
  128. data/lib/mail/parsers/{ragel/date_time.rl → rfc5322_date_time.rl} +8 -1
  129. data/lib/mail/parsers/rfc5322_lexical_tokens.rl +60 -0
  130. data/lib/mail/parsers.rb +17 -24
  131. data/lib/mail/part.rb +8 -5
  132. data/lib/mail/parts_list.rb +31 -14
  133. data/lib/mail/utilities.rb +112 -13
  134. data/lib/mail/values/unicode_tables.dat +0 -0
  135. data/lib/mail/version.rb +8 -15
  136. data/lib/mail/version_specific/ruby_1_8.rb +52 -8
  137. data/lib/mail/version_specific/ruby_1_9.rb +143 -24
  138. data/lib/mail.rb +8 -14
  139. metadata +71 -81
  140. data/CHANGELOG.rdoc +0 -752
  141. data/CONTRIBUTING.md +0 -60
  142. data/Dependencies.txt +0 -2
  143. data/Gemfile +0 -15
  144. data/Rakefile +0 -29
  145. data/TODO.rdoc +0 -9
  146. data/VERSION +0 -4
  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/string/access.rb +0 -145
  150. data/lib/mail/core_extensions/string/multibyte.rb +0 -78
  151. data/lib/mail/multibyte/exceptions.rb +0 -8
  152. data/lib/mail/parsers/ragel/common.rl +0 -184
  153. data/lib/mail/parsers/ragel/parser_info.rb +0 -61
  154. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +0 -14864
  155. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +0 -37
  156. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +0 -751
  157. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +0 -37
  158. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +0 -614
  159. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +0 -37
  160. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +0 -447
  161. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +0 -37
  162. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +0 -825
  163. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +0 -37
  164. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +0 -817
  165. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +0 -37
  166. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +0 -2129
  167. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +0 -37
  168. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +0 -1570
  169. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +0 -37
  170. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +0 -440
  171. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +0 -37
  172. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +0 -564
  173. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +0 -37
  174. data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +0 -51
  175. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +0 -5144
  176. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +0 -37
  177. data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +0 -37
  178. data/lib/mail/parsers/ragel/ruby.rb +0 -39
  179. data/lib/mail/parsers/ragel.rb +0 -17
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  #
3
4
  # = Resent-Cc Field
4
5
  #
@@ -38,8 +39,7 @@ module Mail
38
39
 
39
40
  def initialize(value = nil, charset = 'utf-8')
40
41
  self.charset = charset
41
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
42
- self.parse
42
+ super(CAPITALIZED_FIELD, value, charset)
43
43
  self
44
44
  end
45
45
 
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  #
3
4
  # resent-date = "Resent-Date:" date-time CRLF
4
5
  require 'mail/fields/common/common_date'
@@ -13,10 +14,9 @@ module Mail
13
14
 
14
15
  def initialize(value = nil, charset = 'utf-8')
15
16
  self.charset = charset
16
- if value.blank?
17
+ if Utilities.blank?(value)
17
18
  value = ::DateTime.now.strftime('%a, %d %b %Y %H:%M:%S %z')
18
19
  else
19
- value = strip_field(FIELD_NAME, value)
20
20
  value = ::DateTime.parse(value.to_s).strftime('%a, %d %b %Y %H:%M:%S %z')
21
21
  end
22
22
  super(CAPITALIZED_FIELD, value, charset)
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  #
3
4
  # = Resent-From Field
4
5
  #
@@ -38,8 +39,7 @@ module Mail
38
39
 
39
40
  def initialize(value = nil, charset = 'utf-8')
40
41
  self.charset = charset
41
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
42
- self.parse
42
+ super(CAPITALIZED_FIELD, value, charset)
43
43
  self
44
44
  end
45
45
 
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  #
3
4
  # resent-msg-id = "Resent-Message-ID:" msg-id CRLF
4
5
  require 'mail/fields/common/common_message_id'
@@ -13,7 +14,7 @@ module Mail
13
14
 
14
15
  def initialize(value = nil, charset = 'utf-8')
15
16
  self.charset = charset
16
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
17
+ super(CAPITALIZED_FIELD, value, charset)
17
18
  self.parse
18
19
  self
19
20
  end
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  #
3
4
  # = Resent-Sender Field
4
5
  #
@@ -37,8 +38,7 @@ module Mail
37
38
 
38
39
  def initialize(value = nil, charset = 'utf-8')
39
40
  self.charset = charset
40
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
41
- self.parse
41
+ super(CAPITALIZED_FIELD, value, charset)
42
42
  self
43
43
  end
44
44
 
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  #
3
4
  # = Resent-To Field
4
5
  #
@@ -38,8 +39,7 @@ module Mail
38
39
 
39
40
  def initialize(value = nil, charset = 'utf-8')
40
41
  self.charset = charset
41
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
42
- self.parse
42
+ super(CAPITALIZED_FIELD, value, charset)
43
43
  self
44
44
  end
45
45
 
@@ -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
  #
@@ -40,8 +41,7 @@ module Mail
40
41
  def initialize(value = nil, charset = 'utf-8')
41
42
  value = nil if value == '<>'
42
43
  self.charset = charset
43
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
44
- self.parse
44
+ super(CAPITALIZED_FIELD, value, charset)
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
  #
@@ -38,8 +39,7 @@ module Mail
38
39
 
39
40
  def initialize(value = nil, charset = 'utf-8')
40
41
  self.charset = charset
41
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
42
- self.parse
42
+ super(CAPITALIZED_FIELD, value, charset)
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
@@ -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
@@ -9,7 +10,7 @@ module Mail
9
10
 
10
11
  def initialize(value = nil, charset = 'utf-8')
11
12
  self.charset = charset
12
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
13
+ super(CAPITALIZED_FIELD, value, charset)
13
14
  end
14
15
 
15
16
  end
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  #
3
4
  # = To Field
4
5
  #
@@ -38,8 +39,7 @@ module Mail
38
39
 
39
40
  def initialize(value = nil, charset = 'utf-8')
40
41
  self.charset = charset
41
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
42
- self.parse
42
+ super(CAPITALIZED_FIELD, value, charset)
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
@@ -31,6 +32,12 @@ module Mail
31
32
  else
32
33
  # Ensure we are dealing with a string
33
34
  value = value.to_s
35
+
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
34
41
  end
35
42
 
36
43
  if charset
@@ -66,11 +73,15 @@ module Mail
66
73
  private
67
74
 
68
75
  def do_encode
69
- value.nil? ? '' : "#{wrapped_value}\r\n"
76
+ if value && !value.empty?
77
+ "#{wrapped_value}\r\n"
78
+ else
79
+ ''
80
+ end
70
81
  end
71
82
 
72
83
  def do_decode
73
- value.blank? ? nil : Encodings.decode_encode(value, :decode)
84
+ Utilities.blank?(value) ? nil : Encodings.decode_encode(value, :decode)
74
85
  end
75
86
 
76
87
  # 2.2.3. Long Header Fields
@@ -120,16 +131,16 @@ module Mail
120
131
  def fold(prepend = 0) # :nodoc:
121
132
  encoding = normalized_encoding
122
133
  decoded_string = decoded.to_s
123
- should_encode = decoded_string.not_ascii_only?
134
+ should_encode = !decoded_string.ascii_only?
124
135
  if should_encode
125
136
  first = true
126
137
  words = decoded_string.split(/[ \t]/).map do |word|
127
138
  if first
128
139
  first = !first
129
140
  else
130
- word = " " << word
141
+ word = " #{word}"
131
142
  end
132
- if word.not_ascii_only?
143
+ if !word.ascii_only?
133
144
  word
134
145
  else
135
146
  word.scan(/.{7}|.+$/)
@@ -143,11 +154,18 @@ module Mail
143
154
  while !words.empty?
144
155
  limit = 78 - prepend
145
156
  limit = limit - 7 - encoding.length if should_encode
146
- line = ""
157
+ line = String.new
147
158
  first_word = true
148
159
  while !words.empty?
149
160
  break unless word = words.first.dup
150
- word.encode!(charset) if charset && word.respond_to?(:encode!)
161
+
162
+ # Convert on 1.9+ only since we aren't sure of the current
163
+ # charset encoding on 1.8. We'd need to track internal/external
164
+ # charset on each field.
165
+ if charset && word.respond_to?(:encoding)
166
+ word = Encodings.transcode_charset(word, word.encoding, charset)
167
+ end
168
+
151
169
  word = encode(word) if should_encode
152
170
  word = encode_crlf(word)
153
171
  # Skip to next line if we're going to go past the limit
@@ -178,7 +196,7 @@ module Mail
178
196
  end
179
197
 
180
198
  def encode(value)
181
- value = [value].pack("M").gsub("=\n", '')
199
+ value = [value].pack(CAPITAL_M).gsub(EQUAL_LF, EMPTY)
182
200
  value.gsub!(/"/, '=22')
183
201
  value.gsub!(/\(/, '=28')
184
202
  value.gsub!(/\)/, '=29')
@@ -189,8 +207,8 @@ module Mail
189
207
  end
190
208
 
191
209
  def encode_crlf(value)
192
- value.gsub!("\r", '=0D')
193
- value.gsub!("\n", '=0A')
210
+ value.gsub!(CR, CR_ENCODED)
211
+ value.gsub!(LF, LF_ENCODED)
194
212
  value
195
213
  end
196
214
 
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
 
@@ -49,13 +50,14 @@ module Mail
49
50
  # me the example so we can fix it.
50
51
  def initialize(header_text = nil, charset = nil)
51
52
  @charset = charset
52
- self.raw_source = header_text.to_crlf.lstrip
53
+ self.raw_source = header_text
53
54
  split_header if header_text
54
55
  end
55
56
 
56
57
  def initialize_copy(original)
57
58
  super
58
59
  @fields = @fields.dup
60
+ @fields.map!(&:dup)
59
61
  end
60
62
 
61
63
  # The preserved raw source of the header as you passed it in, untouched
@@ -91,14 +93,15 @@ module Mail
91
93
  # h.fields = ['From: mikel@me.com', 'To: bob@you.com']
92
94
  def fields=(unfolded_fields)
93
95
  @fields = Mail::FieldList.new
94
- warn "Warning: more than #{self.class.maximum_amount} header fields only using the first #{self.class.maximum_amount}" if unfolded_fields.length > self.class.maximum_amount
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
95
97
  unfolded_fields[0..(self.class.maximum_amount-1)].each do |field|
96
98
 
97
- field = Field.new(field, nil, charset)
98
- if limited_field?(field.name) && (selected = select_field_for(field.name)) && selected.any?
99
- selected.first.update(field.name, field.value)
100
- else
101
- @fields << field
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
102
105
  end
103
106
  end
104
107
 
@@ -130,12 +133,13 @@ module Mail
130
133
  # h['To'] #=> 'mikel@me.com'
131
134
  # h['X-Mail-SPAM'] #=> ['15', '20']
132
135
  def [](name)
133
- name = dasherize(name).downcase
136
+ name = dasherize(name)
137
+ name.downcase!
134
138
  selected = select_field_for(name)
135
139
  case
136
140
  when selected.length > 1
137
141
  selected.map { |f| f }
138
- when !selected.blank?
142
+ when !Utilities.blank?(selected)
139
143
  selected.first
140
144
  else
141
145
  nil
@@ -165,11 +169,11 @@ module Mail
165
169
 
166
170
  case
167
171
  # User wants to delete the field
168
- when !selected.blank? && value == nil
172
+ when !Utilities.blank?(selected) && value == nil
169
173
  fields.delete_if { |f| selected.include?(f) }
170
174
 
171
175
  # User wants to change the field
172
- when !selected.blank? && limited_field?(fn)
176
+ when !Utilities.blank?(selected) && limited_field?(fn)
173
177
  selected.first.update(fn, value)
174
178
 
175
179
  # User wants to create the field
@@ -203,7 +207,7 @@ module Mail
203
207
  content-id content-disposition content-location]
204
208
 
205
209
  def encoded
206
- buffer = ''
210
+ buffer = String.new
207
211
  buffer.force_encoding('us-ascii') if buffer.respond_to?(:force_encoding)
208
212
  fields.each do |field|
209
213
  buffer << field.encoded
@@ -246,7 +250,7 @@ module Mail
246
250
  private
247
251
 
248
252
  def raw_source=(val)
249
- @raw_source = val
253
+ @raw_source = ::Mail::Utilities.to_crlf(val).lstrip
250
254
  end
251
255
 
252
256
  # Splits an unfolded and line break cleaned header into individual field
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  # This is an almost cut and paste from ActiveSupport v3.0.6, copied in here so that Mail
4
5
  # itself does not depend on ActiveSupport to avoid versioning conflicts
data/lib/mail/mail.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  module Mail
3
4
 
4
5
  # Allows you to create a new Mail::Message object.
@@ -89,19 +90,11 @@ module Mail
89
90
  # Each mail object inherits the default set in Mail.delivery_method, however, on
90
91
  # a per email basis, you can override the method:
91
92
  #
92
- # mail.delivery_method :sendmail
93
+ # mail.delivery_method :smtp
93
94
  #
94
95
  # Or you can override the method and pass in settings:
95
96
  #
96
- # mail.delivery_method :sendmail, { :address => 'some.host' }
97
- #
98
- # You can also just modify the settings:
99
- #
100
- # mail.delivery_settings = { :address => 'some.host' }
101
- #
102
- # The passed in hash is just merged against the defaults with +merge!+ and the result
103
- # assigned the mail object. So the above example will change only the :address value
104
- # of the global smtp_settings to be 'some.host', keeping all other values
97
+ # mail.delivery_method :smtp, :address => 'some.host'
105
98
  def self.defaults(&block)
106
99
  Configuration.instance.instance_eval(&block)
107
100
  end
@@ -245,9 +238,11 @@ module Mail
245
238
 
246
239
  protected
247
240
 
241
+ RANDOM_TAG='%x%x_%x%x%d%x'
242
+
248
243
  def self.random_tag
249
244
  t = Time.now
250
- sprintf('%x%x_%x%x%d%x',
245
+ sprintf(RANDOM_TAG,
251
246
  t.to_i, t.tv_usec,
252
247
  $$, Thread.current.object_id.abs, self.uniq, rand(255))
253
248
  end
@@ -0,0 +1,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
@@ -42,15 +43,25 @@ module Mail
42
43
 
43
44
  def bcc(recipient_or_list)
44
45
  @blind_copy_recipients ||= []
46
+ @blind_copy_recipients.concat(Array(recipient_or_list))
47
+ self
48
+ end
45
49
 
46
- if recipient_or_list.kind_of?(Array)
47
- @blind_copy_recipients += recipient_or_list
48
- else
49
- @blind_copy_recipients << recipient_or_list
50
- end
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
51
58
  self
52
59
  end
53
60
 
61
+ def with_any_attachments
62
+ @having_attachments = true
63
+ self
64
+ end
54
65
 
55
66
  def with_subject(subject)
56
67
  @subject = subject
@@ -72,6 +83,16 @@ module Mail
72
83
  self
73
84
  end
74
85
 
86
+ def with_html(body)
87
+ @html_part_body = body
88
+ self
89
+ end
90
+
91
+ def with_text(body)
92
+ @text_part_body = body
93
+ self
94
+ end
95
+
75
96
  def description
76
97
  result = "send a matching email"
77
98
  result
@@ -84,7 +105,7 @@ module Mail
84
105
  result
85
106
  end
86
107
 
87
- def negative_failure_message
108
+ def failure_message_when_negated
88
109
  result = "Expected no email to be sent "
89
110
  result += explain_expectations
90
111
  result += dump_deliveries
@@ -92,11 +113,13 @@ module Mail
92
113
  end
93
114
 
94
115
  protected
95
-
116
+
96
117
  def filter_matched_deliveries(deliveries)
97
118
  candidate_deliveries = deliveries
98
-
99
- %w(sender recipients copy_recipients blind_copy_recipients subject subject_matcher body body_matcher).each do |modifier_name|
119
+ modifiers =
120
+ %w(sender recipients copy_recipients blind_copy_recipients subject
121
+ subject_matcher body body_matcher html_part_body text_part_body having_attachments attachments)
122
+ modifiers.each do |modifier_name|
100
123
  next unless instance_variable_defined?("@#{modifier_name}")
101
124
  candidate_deliveries = candidate_deliveries.select{|matching_delivery| self.send("matches_on_#{modifier_name}?", matching_delivery)}
102
125
  end
@@ -128,6 +151,17 @@ module Mail
128
151
  @subject_matcher.match delivery.subject
129
152
  end
130
153
 
154
+ def matches_on_having_attachments?(delivery)
155
+ @having_attachments && delivery.attachments.any? ||
156
+ (!@having_attachments && delivery.attachments.none?)
157
+ end
158
+
159
+ def matches_on_attachments?(delivery)
160
+ @attachments.each_with_index.inject( true ) do |sent_attachments, (attachment, index)|
161
+ sent_attachments &&= (attachment === delivery.attachments[index])
162
+ end
163
+ end
164
+
131
165
  def matches_on_body?(delivery)
132
166
  delivery.body == @body
133
167
  end
@@ -136,6 +170,14 @@ module Mail
136
170
  @body_matcher.match delivery.body.raw_source
137
171
  end
138
172
 
173
+ def matches_on_html_part_body?(delivery)
174
+ delivery.html_part.body == @html_part_body
175
+ end
176
+
177
+ def matches_on_text_part_body?(delivery)
178
+ delivery.text_part.body == @text_part_body
179
+ end
180
+
139
181
  def explain_expectations
140
182
  result = ''
141
183
  result += "from #{@sender} " if instance_variable_defined?('@sender')
@@ -146,6 +188,8 @@ module Mail
146
188
  result += "with subject matching \"#{@subject_matcher}\" " if instance_variable_defined?('@subject_matcher')
147
189
  result += "with body \"#{@body}\" " if instance_variable_defined?('@body')
148
190
  result += "with body matching \"#{@body_matcher}\" " if instance_variable_defined?('@body_matcher')
191
+ result += "with a text part matching \"#{@text_part_body}\" " if instance_variable_defined?('@text_part_body')
192
+ result += "with an HTML part matching \"#{@html_part_body}\" " if instance_variable_defined?('@html_part_body')
149
193
  result
150
194
  end
151
195