mail 2.6.3 → 2.7.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 (178) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +91 -79
  4. data/lib/mail/attachments_list.rb +11 -5
  5. data/lib/mail/body.rb +46 -39
  6. data/lib/mail/check_delivery_params.rb +50 -10
  7. data/lib/mail/configuration.rb +3 -0
  8. data/lib/mail/constants.rb +4 -2
  9. data/lib/mail/core_extensions/smtp.rb +20 -16
  10. data/lib/mail/core_extensions/string.rb +1 -30
  11. data/lib/mail/elements/address.rb +43 -32
  12. data/lib/mail/elements/address_list.rb +11 -18
  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 +3 -1
  32. data/lib/mail/encodings.rb +114 -60
  33. data/lib/mail/envelope.rb +2 -1
  34. data/lib/mail/field.rb +112 -62
  35. data/lib/mail/field_list.rb +1 -0
  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 +5 -11
  43. data/lib/mail/fields/common/common_message_id.rb +3 -2
  44. data/lib/mail/fields/common/parameter_hash.rb +2 -1
  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 +6 -10
  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 +25 -7
  74. data/lib/mail/fields.rb +1 -0
  75. data/lib/mail/header.rb +15 -12
  76. data/lib/mail/indifferent_hash.rb +1 -0
  77. data/lib/mail/mail.rb +3 -10
  78. data/lib/mail/matchers/attachment_matchers.rb +29 -0
  79. data/lib/mail/matchers/has_sent_mail.rb +51 -7
  80. data/lib/mail/message.rb +91 -85
  81. data/lib/mail/multibyte/chars.rb +32 -30
  82. data/lib/mail/multibyte/unicode.rb +21 -16
  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 +33187 -112
  99. data/lib/mail/parsers/address_lists_parser.rl +172 -0
  100. data/lib/mail/parsers/content_disposition_parser.rb +889 -55
  101. data/lib/mail/parsers/content_disposition_parser.rl +82 -0
  102. data/lib/mail/parsers/content_location_parser.rb +801 -23
  103. data/lib/mail/parsers/content_location_parser.rl +71 -0
  104. data/lib/mail/parsers/content_transfer_encoding_parser.rb +511 -22
  105. data/lib/mail/parsers/content_transfer_encoding_parser.rl +64 -0
  106. data/lib/mail/parsers/content_type_parser.rb +1033 -54
  107. data/lib/mail/parsers/content_type_parser.rl +83 -0
  108. data/lib/mail/parsers/date_time_parser.rb +875 -25
  109. data/lib/mail/parsers/date_time_parser.rl +62 -0
  110. data/lib/mail/parsers/envelope_from_parser.rb +3565 -36
  111. data/lib/mail/parsers/envelope_from_parser.rl +82 -0
  112. data/lib/mail/parsers/message_ids_parser.rb +2844 -25
  113. data/lib/mail/parsers/message_ids_parser.rl +82 -0
  114. data/lib/mail/parsers/mime_version_parser.rb +494 -26
  115. data/lib/mail/parsers/mime_version_parser.rl +61 -0
  116. data/lib/mail/parsers/phrase_lists_parser.rb +876 -18
  117. data/lib/mail/parsers/phrase_lists_parser.rl +83 -0
  118. data/lib/mail/parsers/received_parser.rb +8744 -37
  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 +6 -3
  132. data/lib/mail/parts_list.rb +31 -14
  133. data/lib/mail/utilities.rb +107 -8
  134. data/lib/mail/values/unicode_tables.dat +0 -0
  135. data/lib/mail/version.rb +3 -2
  136. data/lib/mail/version_specific/ruby_1_8.rb +50 -6
  137. data/lib/mail/version_specific/ruby_1_9.rb +103 -18
  138. data/lib/mail.rb +5 -12
  139. metadata +33 -57
  140. data/CHANGELOG.rdoc +0 -759
  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/lib/mail/core_extensions/nil.rb +0 -19
  147. data/lib/mail/core_extensions/object.rb +0 -13
  148. data/lib/mail/core_extensions/string/access.rb +0 -145
  149. data/lib/mail/core_extensions/string/multibyte.rb +0 -78
  150. data/lib/mail/multibyte/exceptions.rb +0 -8
  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 -17
data/lib/mail/field.rb CHANGED
@@ -1,4 +1,6 @@
1
+ # frozen_string_literal: true
1
2
  require 'mail/fields'
3
+ require 'mail/constants'
2
4
 
3
5
  # encoding: utf-8
4
6
  module Mail
@@ -82,9 +84,31 @@ module Mail
82
84
 
83
85
  def initialize(element, value, reason)
84
86
  @element = element
85
- @value = value
86
- @reason = reason
87
- super("#{element} can not parse |#{value}|\nReason was: #{reason}")
87
+ @value = to_utf8(value)
88
+ @reason = to_utf8(reason)
89
+ super("#{@element} can not parse |#{@value}|: #{@reason}")
90
+ end
91
+
92
+ private
93
+ def to_utf8(text)
94
+ if text.respond_to?(:force_encoding)
95
+ text.dup.force_encoding(Encoding::UTF_8)
96
+ else
97
+ text
98
+ end
99
+ end
100
+ end
101
+
102
+ class NilParseError < ParseError #:nodoc:
103
+ def initialize(element)
104
+ super element, nil, 'nil is invalid'
105
+ end
106
+ end
107
+
108
+ class IncompleteParseError < ParseError #:nodoc:
109
+ def initialize(element, original_text, unparsed_index)
110
+ parsed_text = to_utf8(original_text[0...unparsed_index])
111
+ super element, original_text, "Only able to parse up to #{parsed_text.inspect}"
88
112
  end
89
113
  end
90
114
 
@@ -92,41 +116,62 @@ module Mail
92
116
  class SyntaxError < FieldError #:nodoc:
93
117
  end
94
118
 
95
- # Accepts a string:
96
- #
97
- # Field.new("field-name: field data")
98
- #
99
- # Or name, value pair:
100
- #
101
- # Field.new("field-name", "value")
119
+ class << self
120
+ # Parse a field from a raw header line:
121
+ #
122
+ # Mail::Field.parse("field-name: field data")
123
+ # # => #<Mail::Field …>
124
+ def parse(field, charset = nil)
125
+ name, value = split(field)
126
+ if name && value
127
+ new name, value, charset
128
+ end
129
+ end
130
+
131
+ def split(raw_field) #:nodoc:
132
+ if raw_field.index(Constants::COLON)
133
+ name, value = raw_field.split(Constants::COLON, 2)
134
+ name.rstrip!
135
+ if name =~ /\A#{Constants::FIELD_NAME}\z/
136
+ [ name.rstrip, value.strip ]
137
+ else
138
+ Kernel.warn "WARNING: Ignoring unparsable header #{raw_field.inspect}: invalid header name syntax: #{name.inspect}"
139
+ nil
140
+ end
141
+ else
142
+ raw_field.strip
143
+ end
144
+ rescue => error
145
+ warn "WARNING: Ignoring unparsable header #{raw_field.inspect}: #{error.class}: #{error.message}"
146
+ nil
147
+ end
148
+ end
149
+
150
+ # Create a field by name and optional value:
102
151
  #
103
- # Or a name by itself:
152
+ # Mail::Field.new("field-name", "value")
153
+ # # => #<Mail::Field …>
104
154
  #
105
- # Field.new("field-name")
155
+ # Values that aren't strings or arrays are coerced to Strings with `#to_s`.
106
156
  #
107
- # Note, does not want a terminating carriage return. Returns
108
- # self appropriately parsed. If value is not a string, then
109
- # it will be passed through as is, for example, content-type
110
- # field can accept an array with the type and a hash of
111
- # parameters:
157
+ # Mail::Field.new("field-name", 1234)
158
+ # # => #<Mail::Field …>
112
159
  #
113
- # Field.new('content-type', ['text', 'plain', {:charset => 'UTF-8'}])
160
+ # Mail::Field.new('content-type', ['text', 'plain', {:charset => 'UTF-8'}])
161
+ # # => #<Mail::Field …>
114
162
  def initialize(name, value = nil, charset = 'utf-8')
115
163
  case
116
- when name.index(COLON) # Field.new("field-name: field data")
117
- @charset = value.blank? ? charset : value
118
- @name = name[FIELD_PREFIX]
119
- @raw_value = name
120
- @value = nil
121
- when value.blank? # Field.new("field-name")
164
+ when name.index(COLON)
165
+ Kernel.warn 'Passing an unparsed header field to Mail::Field.new is deprecated and will be removed in Mail 2.8.0. Use Mail::Field.parse instead.'
166
+ @name, @unparsed_value = self.class.split(name)
167
+ @charset = Utilities.blank?(value) ? charset : value
168
+ when Utilities.blank?(value)
122
169
  @name = name
123
- @value = nil
124
- @raw_value = nil
170
+ @unparsed_value = nil
125
171
  @charset = charset
126
- else # Field.new("field-name", "value")
172
+ else
127
173
  @name = name
128
- @value = value
129
- @raw_value = nil
174
+ @unparsed_value = value
130
175
  @charset = charset
131
176
  end
132
177
  @name = FIELD_NAME_MAP[@name.to_s.downcase] || @name
@@ -137,8 +182,7 @@ module Mail
137
182
  end
138
183
 
139
184
  def field
140
- _, @value = split(@raw_value) if @raw_value && !@value
141
- @field ||= create_field(@name, @value, @charset)
185
+ @field ||= create_field(@name, @unparsed_value, @charset)
142
186
  end
143
187
 
144
188
  def name
@@ -168,15 +212,19 @@ module Mail
168
212
  end
169
213
 
170
214
  def same( other )
215
+ return false unless other.kind_of?(self.class)
171
216
  match_to_s(other.name, self.name)
172
217
  end
173
218
 
219
+ def ==( other )
220
+ return false unless other.kind_of?(self.class)
221
+ match_to_s(other.name, self.name) && match_to_s(other.value, self.value)
222
+ end
223
+
174
224
  def responsible_for?( val )
175
225
  name.to_s.casecmp(val.to_s) == 0
176
226
  end
177
227
 
178
- alias_method :==, :same
179
-
180
228
  def <=>( other )
181
229
  self.field_order_id <=> other.field_order_id
182
230
  end
@@ -189,6 +237,16 @@ module Mail
189
237
  field.send(name, *args, &block)
190
238
  end
191
239
 
240
+ if RUBY_VERSION >= '1.9.2'
241
+ def respond_to_missing?(method_name, include_private)
242
+ field.respond_to?(method_name, include_private) || super
243
+ end
244
+ else
245
+ def respond_to?(method_name, include_private = false)
246
+ field.respond_to?(method_name, include_private) || super
247
+ end
248
+ end
249
+
192
250
  FIELD_ORDER = %w[ return-path received
193
251
  resent-date resent-from resent-sender resent-to
194
252
  resent-cc resent-bcc resent-message-id
@@ -202,11 +260,26 @@ module Mail
202
260
 
203
261
  private
204
262
 
205
- def split(raw_field)
206
- match_data = raw_field.mb_chars.match(FIELD_SPLIT)
207
- [match_data[1].to_s.mb_chars.strip, match_data[2].to_s.mb_chars.strip.to_s]
208
- rescue
209
- STDERR.puts "WARNING: Could not parse (and so ignoring) '#{raw_field}'"
263
+ def create_field(name, value, charset)
264
+ new_field(name, value, charset)
265
+ rescue Mail::Field::ParseError => e
266
+ field = Mail::UnstructuredField.new(name, value)
267
+ field.errors << [name, value, e]
268
+ field
269
+ end
270
+
271
+ def new_field(name, value, charset)
272
+ value = unfold(value) if value.is_a?(String)
273
+
274
+ if klass = field_class_for(name)
275
+ klass.new(value, charset)
276
+ else
277
+ OptionalField.new(name, value, charset)
278
+ end
279
+ end
280
+
281
+ def field_class_for(name)
282
+ FIELDS_MAP[name.to_s.downcase]
210
283
  end
211
284
 
212
285
  # 2.2.3. Long Header Fields
@@ -218,30 +291,7 @@ module Mail
218
291
  # treated in its unfolded form for further syntactic and semantic
219
292
  # evaluation.
220
293
  def unfold(string)
221
- string.gsub(/[\r\n \t]+/m, ' ')
222
- end
223
-
224
- def create_field(name, value, charset)
225
- value = unfold(value) if value.is_a?(String)
226
-
227
- begin
228
- new_field(name, value, charset)
229
- rescue Mail::Field::ParseError => e
230
- field = Mail::UnstructuredField.new(name, value)
231
- field.errors << [name, value, e]
232
- field
233
- end
234
- end
235
-
236
- def new_field(name, value, charset)
237
- lower_case_name = name.to_s.downcase
238
- if field_klass = FIELDS_MAP[lower_case_name]
239
- field_klass.new(value, charset)
240
- else
241
- OptionalField.new(name, value, charset)
242
- end
294
+ string.gsub(/#{CRLF}(#{WSP})/m, '\1')
243
295
  end
244
-
245
296
  end
246
-
247
297
  end
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  module Mail
3
4
 
4
5
  # Field List class provides an enhanced array that keeps a list of
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  #
3
4
  # = Blind Carbon Copy Field
4
5
  #
@@ -36,16 +37,27 @@ module Mail
36
37
  FIELD_NAME = 'bcc'
37
38
  CAPITALIZED_FIELD = 'Bcc'
38
39
 
39
- def initialize(value = '', charset = 'utf-8')
40
+ def initialize(value = nil, charset = 'utf-8')
40
41
  @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
 
46
- # Bcc field should never be :encoded
46
+ def include_in_headers=(include_in_headers)
47
+ @include_in_headers = include_in_headers
48
+ end
49
+
50
+ def include_in_headers
51
+ defined?(@include_in_headers) ? @include_in_headers : self.include_in_headers = false
52
+ end
53
+
54
+ # Bcc field should not be :encoded by default
47
55
  def encoded
48
- ''
56
+ if include_in_headers
57
+ do_encode(CAPITALIZED_FIELD)
58
+ else
59
+ ''
60
+ end
49
61
  end
50
62
 
51
63
  def decoded
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  #
3
4
  # = Carbon Copy 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
  # = Comments Field
4
5
  #
@@ -32,7 +33,7 @@ module Mail
32
33
 
33
34
  def initialize(value = nil, charset = 'utf-8')
34
35
  @charset = charset
35
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value))
36
+ super(CAPITALIZED_FIELD, value)
36
37
  self.parse
37
38
  self
38
39
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Mail
2
3
 
3
4
  class AddressContainer < Array
@@ -7,10 +8,10 @@ module Mail
7
8
  super(list)
8
9
  end
9
10
 
10
- def << (address)
11
+ def <<(address)
11
12
  @field << address
12
13
  end
13
14
 
14
15
  end
15
16
 
16
- end
17
+ end
@@ -1,25 +1,32 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  require 'mail/fields/common/address_container'
3
4
 
4
5
  module Mail
5
6
  module CommonAddress # :nodoc:
6
-
7
7
  def parse(val = value)
8
- unless val.blank?
8
+ unless Utilities.blank?(val)
9
9
  @address_list = AddressList.new(encode_if_needed(val))
10
10
  else
11
11
  nil
12
12
  end
13
13
  end
14
-
14
+
15
15
  def charset
16
16
  @charset
17
17
  end
18
-
19
- def encode_if_needed(val)
20
- Encodings.address_encode(val, charset)
18
+
19
+ def encode_if_needed(val) #:nodoc:
20
+ # Need to join arrays of addresses into a single value
21
+ if val.kind_of?(Array)
22
+ val.compact.map { |a| encode_if_needed a }.join(', ')
23
+
24
+ # Pass through UTF-8; encode non-UTF-8.
25
+ else
26
+ utf8_if_needed(val) || Encodings.encode_non_usascii(val, charset)
27
+ end
21
28
  end
22
-
29
+
23
30
  # Allows you to iterate through each address object in the address_list
24
31
  def each
25
32
  address_list.addresses.each do |address|
@@ -84,10 +91,10 @@ module Mail
84
91
  case
85
92
  when val.nil?
86
93
  raise ArgumentError, "Need to pass an address to <<"
87
- when val.blank?
94
+ when Utilities.blank?(val)
88
95
  parse(encoded)
89
96
  else
90
- self.value = [self.value, val].reject {|a| a.blank? }.join(", ")
97
+ self.value = [self.value, val].reject {|a| Utilities.blank?(a) }.join(", ")
91
98
  end
92
99
  end
93
100
 
@@ -97,24 +104,43 @@ module Mail
97
104
  end
98
105
 
99
106
  private
100
-
107
+
108
+ if 'string'.respond_to?(:encoding)
109
+ # Pass through UTF-8 addresses
110
+ def utf8_if_needed(val)
111
+ if charset =~ /\AUTF-?8\z/i
112
+ val
113
+ elsif val.encoding == Encoding::UTF_8
114
+ val
115
+ elsif (utf8 = val.dup.force_encoding(Encoding::UTF_8)).valid_encoding?
116
+ utf8
117
+ end
118
+ end
119
+ else
120
+ def utf8_if_needed(val)
121
+ if charset =~ /\AUTF-?8\z/i
122
+ val
123
+ end
124
+ end
125
+ end
126
+
101
127
  def do_encode(field_name)
102
- return '' if value.blank?
128
+ return '' if Utilities.blank?(value)
103
129
  address_array = address_list.addresses.reject { |a| encoded_group_addresses.include?(a.encoded) }.compact.map { |a| a.encoded }
104
130
  address_text = address_array.join(", \r\n\s")
105
131
  group_array = groups.map { |k,v| "#{k}: #{v.map { |a| a.encoded }.join(", \r\n\s")};" }
106
132
  group_text = group_array.join(" \r\n\s")
107
- return_array = [address_text, group_text].reject { |a| a.blank? }
133
+ return_array = [address_text, group_text].reject { |a| Utilities.blank?(a) }
108
134
  "#{field_name}: #{return_array.join(", \r\n\s")}\r\n"
109
135
  end
110
136
 
111
137
  def do_decode
112
- return nil if value.blank?
138
+ return nil if Utilities.blank?(value)
113
139
  address_array = address_list.addresses.reject { |a| decoded_group_addresses.include?(a.decoded) }.map { |a| a.decoded }
114
140
  address_text = address_array.join(", ")
115
141
  group_array = groups.map { |k,v| "#{k}: #{v.map { |a| a.decoded }.join(", ")};" }
116
142
  group_text = group_array.join(" ")
117
- return_array = [address_text, group_text].reject { |a| a.blank? }
143
+ return_array = [address_text, group_text].reject { |a| Utilities.blank?(a) }
118
144
  return_array.join(", ")
119
145
  end
120
146
 
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  module Mail
3
4
  module CommonDate # :nodoc:
4
5
  # Returns a date time object of the parsed date
@@ -11,7 +12,7 @@ module Mail
11
12
  end
12
13
 
13
14
  def parse(val = value)
14
- unless val.blank?
15
+ unless Utilities.blank?(val)
15
16
  @element = Mail::DateTimeElement.new(val)
16
17
  else
17
18
  nil
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  module Mail
3
4
  module CommonField # :nodoc:
4
5
  include Mail::Constants
@@ -13,9 +14,8 @@ module Mail
13
14
 
14
15
  def value=(value)
15
16
  @length = nil
16
- @tree = nil
17
17
  @element = nil
18
- @value = value
18
+ @value = value.is_a?(Array) ? value : value.to_s
19
19
  end
20
20
 
21
21
  def value
@@ -40,18 +40,12 @@ module Mail
40
40
 
41
41
  private
42
42
 
43
- def strip_field(field_name, value)
44
- if value.is_a?(Array)
45
- value
46
- else
47
- value.to_s.sub(/#{field_name}:\s+/i, EMPTY)
48
- end
49
- end
50
-
51
43
  FILENAME_RE = /\b(filename|name)=([^;"\r\n]+\s[^;"\r\n]+)/
52
44
  def ensure_filename_quoted(value)
53
45
  if value.is_a?(String)
54
- value.sub! FILENAME_RE, '\1="\2"'
46
+ value.sub FILENAME_RE, '\1="\2"'
47
+ else
48
+ value
55
49
  end
56
50
  end
57
51
  end
@@ -1,12 +1,13 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  module Mail
3
4
  module CommonMessageId # :nodoc:
4
5
  def element
5
- @element ||= Mail::MessageIdsElement.new(value) unless value.blank?
6
+ @element ||= Mail::MessageIdsElement.new(value) unless Utilities.blank?(value)
6
7
  end
7
8
 
8
9
  def parse(val = value)
9
- unless val.blank?
10
+ unless Utilities.blank?(val)
10
11
  @element = Mail::MessageIdsElement.new(val)
11
12
  else
12
13
  nil
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  module Mail
3
4
 
4
5
  # ParameterHash is an intelligent Hash that allows you to add
@@ -29,7 +30,7 @@ module Mail
29
30
  super(exact || key_name)
30
31
  else # Dealing with a multiple value pair or a single encoded value pair
31
32
  string = pairs.sort { |a,b| a.first.to_s <=> b.first.to_s }.map { |v| v.last }.join('')
32
- if mt = string.match(/([\w\-]+)'(\w\w)'(.*)/)
33
+ if mt = string.match(/([\w\-]+)?'(\w\w)?'(.*)/)
33
34
  string = mt[3]
34
35
  encoding = mt[1]
35
36
  else
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  #
3
4
  #
4
5
  #
@@ -10,7 +11,7 @@ module Mail
10
11
 
11
12
  def initialize(value = nil, charset = 'utf-8')
12
13
  self.charset = charset
13
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
14
+ super(CAPITALIZED_FIELD, value, charset)
14
15
  self.parse
15
16
  self
16
17
  end
@@ -1,26 +1,27 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  require 'mail/fields/common/parameter_hash'
3
4
 
4
5
  module Mail
5
6
  class ContentDispositionField < StructuredField
6
-
7
+
7
8
  FIELD_NAME = 'content-disposition'
8
9
  CAPITALIZED_FIELD = 'Content-Disposition'
9
-
10
+
10
11
  def initialize(value = nil, charset = 'utf-8')
11
12
  self.charset = charset
12
- ensure_filename_quoted(value)
13
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
13
+ value = ensure_filename_quoted(value)
14
+ super(CAPITALIZED_FIELD, value, charset)
14
15
  self.parse
15
16
  self
16
17
  end
17
-
18
+
18
19
  def parse(val = value)
19
- unless val.blank?
20
+ unless Utilities.blank?(val)
20
21
  @element = Mail::ContentDispositionElement.new(val)
21
22
  end
22
23
  end
23
-
24
+
24
25
  def element
25
26
  @element ||= Mail::ContentDispositionElement.new(value)
26
27
  end
@@ -28,20 +29,20 @@ module Mail
28
29
  def disposition_type
29
30
  element.disposition_type
30
31
  end
31
-
32
+
32
33
  def parameters
33
34
  @parameters = ParameterHash.new
34
- element.parameters.each { |p| @parameters.merge!(p) }
35
+ element.parameters.each { |p| @parameters.merge!(p) } unless element.parameters.nil?
35
36
  @parameters
36
37
  end
37
38
 
38
39
  def filename
39
40
  case
40
- when !parameters['filename'].blank?
41
+ when parameters['filename']
41
42
  @filename = parameters['filename']
42
- when !parameters['name'].blank?
43
+ when parameters['name']
43
44
  @filename = parameters['name']
44
- else
45
+ else
45
46
  @filename = nil
46
47
  end
47
48
  @filename
@@ -56,7 +57,7 @@ module Mail
56
57
  end
57
58
  "#{CAPITALIZED_FIELD}: #{disposition_type}" + p
58
59
  end
59
-
60
+
60
61
  def decoded
61
62
  if parameters.length > 0
62
63
  p = "; #{parameters.decoded}"
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  #
3
4
  #
4
5
  #
@@ -11,18 +12,18 @@ module Mail
11
12
  def initialize(value = nil, charset = 'utf-8')
12
13
  self.charset = charset
13
14
  @uniq = 1
14
- if value.blank?
15
+ if Utilities.blank?(value)
15
16
  value = generate_content_id
16
17
  else
17
- value = strip_field(FIELD_NAME, value)
18
+ value = value.to_s
18
19
  end
19
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
20
+ super(CAPITALIZED_FIELD, value, charset)
20
21
  self.parse
21
22
  self
22
23
  end
23
24
 
24
25
  def parse(val = value)
25
- unless val.blank?
26
+ unless Utilities.blank?(val)
26
27
  @element = Mail::MessageIdsElement.new(val)
27
28
  end
28
29
  end
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  #
3
4
  #
4
5
  #
@@ -10,13 +11,13 @@ module Mail
10
11
 
11
12
  def initialize(value = nil, charset = 'utf-8')
12
13
  self.charset = charset
13
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
14
+ super(CAPITALIZED_FIELD, value, charset)
14
15
  self.parse
15
16
  self
16
17
  end
17
18
 
18
19
  def parse(val = value)
19
- unless val.blank?
20
+ unless Utilities.blank?(val)
20
21
  @element = Mail::ContentLocationElement.new(val)
21
22
  end
22
23
  end
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  #
3
4
  #
4
5
  #
@@ -12,13 +13,13 @@ module Mail
12
13
  self.charset = charset
13
14
  value = '7bit' if value.to_s =~ /7-?bits?/i
14
15
  value = '8bit' if value.to_s =~ /8-?bits?/i
15
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
16
+ super(CAPITALIZED_FIELD, value, charset)
16
17
  self.parse
17
18
  self
18
19
  end
19
20
 
20
21
  def parse(val = value)
21
- unless val.blank?
22
+ unless Utilities.blank?(val)
22
23
  @element = Mail::ContentTransferEncodingElement.new(val)
23
24
  end
24
25
  end