mail 2.6.6 → 2.7.1

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 (160) hide show
  1. checksums.yaml +5 -5
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +74 -90
  4. data/lib/mail/attachments_list.rb +8 -4
  5. data/lib/mail/body.rb +50 -38
  6. data/lib/mail/check_delivery_params.rb +8 -6
  7. data/lib/mail/configuration.rb +2 -0
  8. data/lib/mail/constants.rb +1 -1
  9. data/lib/mail/core_extensions/smtp.rb +19 -16
  10. data/lib/mail/core_extensions/string.rb +0 -4
  11. data/lib/mail/elements/address.rb +28 -22
  12. data/lib/mail/elements/address_list.rb +10 -18
  13. data/lib/mail/elements/content_disposition_element.rb +8 -15
  14. data/lib/mail/elements/content_location_element.rb +5 -10
  15. data/lib/mail/elements/content_transfer_encoding_element.rb +5 -10
  16. data/lib/mail/elements/content_type_element.rb +8 -19
  17. data/lib/mail/elements/date_time_element.rb +6 -14
  18. data/lib/mail/elements/envelope_from_element.rb +14 -21
  19. data/lib/mail/elements/message_ids_element.rb +8 -12
  20. data/lib/mail/elements/mime_version_element.rb +6 -14
  21. data/lib/mail/elements/phrase_list.rb +6 -9
  22. data/lib/mail/elements/received_element.rb +9 -15
  23. data/lib/mail/encodings/7bit.rb +5 -15
  24. data/lib/mail/encodings/8bit.rb +2 -21
  25. data/lib/mail/encodings/base64.rb +11 -12
  26. data/lib/mail/encodings/binary.rb +3 -22
  27. data/lib/mail/encodings/identity.rb +24 -0
  28. data/lib/mail/encodings/quoted_printable.rb +6 -6
  29. data/lib/mail/encodings/transfer_encoding.rb +38 -29
  30. data/lib/mail/encodings/unix_to_unix.rb +3 -1
  31. data/lib/mail/encodings.rb +99 -43
  32. data/lib/mail/envelope.rb +1 -1
  33. data/lib/mail/field.rb +96 -59
  34. data/lib/mail/fields/bcc_field.rb +2 -2
  35. data/lib/mail/fields/cc_field.rb +1 -1
  36. data/lib/mail/fields/comments_field.rb +1 -1
  37. data/lib/mail/fields/common/common_address.rb +32 -7
  38. data/lib/mail/fields/common/common_field.rb +1 -10
  39. data/lib/mail/fields/common/parameter_hash.rb +1 -1
  40. data/lib/mail/fields/content_description_field.rb +1 -1
  41. data/lib/mail/fields/content_disposition_field.rb +3 -3
  42. data/lib/mail/fields/content_id_field.rb +2 -2
  43. data/lib/mail/fields/content_location_field.rb +1 -1
  44. data/lib/mail/fields/content_transfer_encoding_field.rb +1 -1
  45. data/lib/mail/fields/content_type_field.rb +4 -9
  46. data/lib/mail/fields/date_field.rb +2 -3
  47. data/lib/mail/fields/from_field.rb +1 -1
  48. data/lib/mail/fields/in_reply_to_field.rb +1 -1
  49. data/lib/mail/fields/keywords_field.rb +1 -1
  50. data/lib/mail/fields/message_id_field.rb +1 -1
  51. data/lib/mail/fields/mime_version_field.rb +1 -1
  52. data/lib/mail/fields/optional_field.rb +4 -1
  53. data/lib/mail/fields/received_field.rb +1 -1
  54. data/lib/mail/fields/references_field.rb +1 -1
  55. data/lib/mail/fields/reply_to_field.rb +1 -1
  56. data/lib/mail/fields/resent_bcc_field.rb +1 -1
  57. data/lib/mail/fields/resent_cc_field.rb +1 -1
  58. data/lib/mail/fields/resent_date_field.rb +0 -1
  59. data/lib/mail/fields/resent_from_field.rb +1 -1
  60. data/lib/mail/fields/resent_message_id_field.rb +1 -1
  61. data/lib/mail/fields/resent_sender_field.rb +1 -1
  62. data/lib/mail/fields/resent_to_field.rb +1 -1
  63. data/lib/mail/fields/return_path_field.rb +1 -1
  64. data/lib/mail/fields/sender_field.rb +1 -1
  65. data/lib/mail/fields/subject_field.rb +1 -1
  66. data/lib/mail/fields/to_field.rb +1 -1
  67. data/lib/mail/fields/unstructured_field.rb +21 -4
  68. data/lib/mail/header.rb +10 -8
  69. data/lib/mail/mail.rb +2 -10
  70. data/lib/mail/matchers/has_sent_mail.rb +21 -1
  71. data/lib/mail/message.rb +78 -68
  72. data/lib/mail/multibyte/chars.rb +29 -28
  73. data/lib/mail/multibyte/unicode.rb +10 -10
  74. data/lib/mail/multibyte.rb +64 -15
  75. data/lib/mail/network/delivery_methods/logger_delivery.rb +37 -0
  76. data/lib/mail/network/delivery_methods/sendmail.rb +8 -5
  77. data/lib/mail/network/delivery_methods/smtp.rb +58 -49
  78. data/lib/mail/network/delivery_methods/smtp_connection.rb +9 -1
  79. data/lib/mail/network/retriever_methods/imap.rb +18 -5
  80. data/lib/mail/network/retriever_methods/pop3.rb +3 -1
  81. data/lib/mail/network.rb +1 -0
  82. data/lib/mail/parser_tools.rb +15 -0
  83. data/lib/mail/parsers/address_lists_parser.rb +33207 -104
  84. data/lib/mail/parsers/address_lists_parser.rl +172 -0
  85. data/lib/mail/parsers/content_disposition_parser.rb +876 -49
  86. data/lib/mail/parsers/content_disposition_parser.rl +82 -0
  87. data/lib/mail/parsers/content_location_parser.rb +803 -23
  88. data/lib/mail/parsers/content_location_parser.rl +71 -0
  89. data/lib/mail/parsers/content_transfer_encoding_parser.rb +501 -19
  90. data/lib/mail/parsers/content_transfer_encoding_parser.rl +64 -0
  91. data/lib/mail/parsers/content_type_parser.rb +1023 -48
  92. data/lib/mail/parsers/content_type_parser.rl +83 -0
  93. data/lib/mail/parsers/date_time_parser.rb +870 -24
  94. data/lib/mail/parsers/date_time_parser.rl +62 -0
  95. data/lib/mail/parsers/envelope_from_parser.rb +3569 -34
  96. data/lib/mail/parsers/envelope_from_parser.rl +82 -0
  97. data/lib/mail/parsers/message_ids_parser.rb +2839 -25
  98. data/lib/mail/parsers/message_ids_parser.rl +82 -0
  99. data/lib/mail/parsers/mime_version_parser.rb +491 -26
  100. data/lib/mail/parsers/mime_version_parser.rl +61 -0
  101. data/lib/mail/parsers/phrase_lists_parser.rb +860 -18
  102. data/lib/mail/parsers/phrase_lists_parser.rl +83 -0
  103. data/lib/mail/parsers/received_parser.rb +8764 -37
  104. data/lib/mail/parsers/received_parser.rl +84 -0
  105. data/lib/mail/parsers/rfc2045_content_transfer_encoding.rl +13 -0
  106. data/lib/mail/parsers/rfc2045_content_type.rl +25 -0
  107. data/lib/mail/parsers/rfc2045_mime.rl +16 -0
  108. data/lib/mail/parsers/rfc2183_content_disposition.rl +15 -0
  109. data/lib/mail/parsers/rfc3629_utf8.rl +19 -0
  110. data/lib/mail/parsers/rfc5234_abnf_core_rules.rl +22 -0
  111. data/lib/mail/parsers/rfc5322.rl +59 -0
  112. data/lib/mail/parsers/rfc5322_address.rl +72 -0
  113. data/lib/mail/parsers/{ragel/date_time.rl → rfc5322_date_time.rl} +8 -1
  114. data/lib/mail/parsers/rfc5322_lexical_tokens.rl +60 -0
  115. data/lib/mail/parsers.rb +16 -24
  116. data/lib/mail/part.rb +3 -3
  117. data/lib/mail/parts_list.rb +5 -6
  118. data/lib/mail/utilities.rb +59 -28
  119. data/lib/mail/version.rb +2 -2
  120. data/lib/mail/version_specific/ruby_1_8.rb +40 -3
  121. data/lib/mail/version_specific/ruby_1_9.rb +61 -9
  122. data/lib/mail.rb +3 -16
  123. metadata +44 -53
  124. data/CHANGELOG.rdoc +0 -803
  125. data/CONTRIBUTING.md +0 -60
  126. data/Dependencies.txt +0 -2
  127. data/Gemfile +0 -14
  128. data/Rakefile +0 -29
  129. data/TODO.rdoc +0 -9
  130. data/lib/mail/core_extensions/string/access.rb +0 -146
  131. data/lib/mail/core_extensions/string/multibyte.rb +0 -79
  132. data/lib/mail/multibyte/exceptions.rb +0 -9
  133. data/lib/mail/parsers/ragel/common.rl +0 -185
  134. data/lib/mail/parsers/ragel/parser_info.rb +0 -61
  135. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +0 -14864
  136. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +0 -37
  137. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +0 -751
  138. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +0 -37
  139. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +0 -614
  140. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +0 -37
  141. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +0 -447
  142. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +0 -37
  143. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +0 -825
  144. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +0 -37
  145. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +0 -817
  146. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +0 -37
  147. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +0 -2149
  148. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +0 -37
  149. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +0 -1570
  150. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +0 -37
  151. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +0 -440
  152. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +0 -37
  153. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +0 -564
  154. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +0 -37
  155. data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +0 -51
  156. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +0 -5144
  157. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +0 -37
  158. data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +0 -37
  159. data/lib/mail/parsers/ragel/ruby.rb +0 -40
  160. data/lib/mail/parsers/ragel.rb +0 -18
@@ -4,7 +4,6 @@ require 'mail/fields/common/address_container'
4
4
 
5
5
  module Mail
6
6
  module CommonAddress # :nodoc:
7
-
8
7
  def parse(val = value)
9
8
  unless Utilities.blank?(val)
10
9
  @address_list = AddressList.new(encode_if_needed(val))
@@ -12,15 +11,22 @@ module Mail
12
11
  nil
13
12
  end
14
13
  end
15
-
14
+
16
15
  def charset
17
16
  @charset
18
17
  end
19
-
20
- def encode_if_needed(val)
21
- 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
22
28
  end
23
-
29
+
24
30
  # Allows you to iterate through each address object in the address_list
25
31
  def each
26
32
  address_list.addresses.each do |address|
@@ -98,7 +104,26 @@ module Mail
98
104
  end
99
105
 
100
106
  private
101
-
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
+
102
127
  def do_encode(field_name)
103
128
  return '' if Utilities.blank?(value)
104
129
  address_array = address_list.addresses.reject { |a| encoded_group_addresses.include?(a.encoded) }.compact.map { |a| a.encoded }
@@ -14,9 +14,8 @@ module Mail
14
14
 
15
15
  def value=(value)
16
16
  @length = nil
17
- @tree = nil
18
17
  @element = nil
19
- @value = value
18
+ @value = value.is_a?(Array) ? value : value.to_s
20
19
  end
21
20
 
22
21
  def value
@@ -41,14 +40,6 @@ module Mail
41
40
 
42
41
  private
43
42
 
44
- def strip_field(field_name, value)
45
- if value.is_a?(Array)
46
- value
47
- else
48
- value.to_s.sub(/\A#{field_name}:\s+/i, EMPTY)
49
- end
50
- end
51
-
52
43
  FILENAME_RE = /\b(filename|name)=([^;"\r\n]+\s[^;"\r\n]+)/
53
44
  def ensure_filename_quoted(value)
54
45
  if value.is_a?(String)
@@ -30,7 +30,7 @@ module Mail
30
30
  super(exact || key_name)
31
31
  else # Dealing with a multiple value pair or a single encoded value pair
32
32
  string = pairs.sort { |a,b| a.first.to_s <=> b.first.to_s }.map { |v| v.last }.join('')
33
- if mt = string.match(/([\w\-]+)'(\w\w)'(.*)/)
33
+ if mt = string.match(/([\w\-]+)?'(\w\w)?'(.*)/)
34
34
  string = mt[3]
35
35
  encoding = mt[1]
36
36
  else
@@ -11,7 +11,7 @@ module Mail
11
11
 
12
12
  def initialize(value = nil, charset = 'utf-8')
13
13
  self.charset = charset
14
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
14
+ super(CAPITALIZED_FIELD, value, charset)
15
15
  self.parse
16
16
  self
17
17
  end
@@ -11,7 +11,7 @@ module Mail
11
11
  def initialize(value = nil, charset = 'utf-8')
12
12
  self.charset = charset
13
13
  value = ensure_filename_quoted(value)
14
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
14
+ super(CAPITALIZED_FIELD, value, charset)
15
15
  self.parse
16
16
  self
17
17
  end
@@ -38,9 +38,9 @@ module Mail
38
38
 
39
39
  def filename
40
40
  case
41
- when !Utilities.blank?(parameters['filename'])
41
+ when parameters['filename']
42
42
  @filename = parameters['filename']
43
- when !Utilities.blank?(parameters['name'])
43
+ when parameters['name']
44
44
  @filename = parameters['name']
45
45
  else
46
46
  @filename = nil
@@ -15,9 +15,9 @@ module Mail
15
15
  if Utilities.blank?(value)
16
16
  value = generate_content_id
17
17
  else
18
- value = strip_field(FIELD_NAME, value)
18
+ value = value.to_s
19
19
  end
20
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
20
+ super(CAPITALIZED_FIELD, value, charset)
21
21
  self.parse
22
22
  self
23
23
  end
@@ -11,7 +11,7 @@ module Mail
11
11
 
12
12
  def initialize(value = nil, charset = 'utf-8')
13
13
  self.charset = charset
14
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
14
+ super(CAPITALIZED_FIELD, value, charset)
15
15
  self.parse
16
16
  self
17
17
  end
@@ -13,7 +13,7 @@ module Mail
13
13
  self.charset = charset
14
14
  value = '7bit' if value.to_s =~ /7-?bits?/i
15
15
  value = '8bit' if value.to_s =~ /8-?bits?/i
16
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
16
+ super(CAPITALIZED_FIELD, value, charset)
17
17
  self.parse
18
18
  self
19
19
  end
@@ -18,7 +18,7 @@ module Mail
18
18
  @main_type = nil
19
19
  @sub_type = nil
20
20
  @parameters = nil
21
- value = strip_field(FIELD_NAME, value)
21
+ value = value.to_s
22
22
  end
23
23
  value = ensure_filename_quoted(value)
24
24
  super(CAPITALIZED_FIELD, value, charset)
@@ -145,9 +145,7 @@ module Mail
145
145
  # TODO: check if there are cases where whitespace is not a separator
146
146
  val = val.
147
147
  gsub(/\s*=\s*/,'='). # remove whitespaces around equal sign
148
- tr(' ',';').
149
- squeeze(';').
150
- gsub(';', '; '). #use '; ' as a separator (or EOL)
148
+ gsub(/[; ]+/, '; '). #use '; ' as a separator (or EOL)
151
149
  gsub(/;\s*$/,'') #remove trailing to keep examples below
152
150
 
153
151
  if val =~ /(boundary=(\S*))/i
@@ -157,9 +155,6 @@ module Mail
157
155
  end
158
156
 
159
157
  case
160
- when val.chomp =~ /^\s*([\w\-]+)\/([\w\-]+)\s*;;+(.*)$/i
161
- # Handles 'text/plain;; format="flowed"' (double semi colon)
162
- "#{$1}/#{$2}; #{$3}"
163
158
  when val.chomp =~ /^\s*([\w\-]+)\/([\w\-]+)\s*;\s?(ISO[\w\-]+)$/i
164
159
  # Microsoft helper:
165
160
  # Handles 'type/subtype;ISO-8559-1'
@@ -180,13 +175,13 @@ module Mail
180
175
  # and: audio/x-midi;\r\n\sname=Part .exe
181
176
  params = $2.to_s.split(/\s+/)
182
177
  params = params.map { |i| i.to_s.chomp.strip }
183
- params = params.map { |i| i.split(/\s*\=\s*/) }
178
+ params = params.map { |i| i.split(/\s*\=\s*/, 2) }
184
179
  params = params.map { |i| "#{i[0]}=#{dquote(i[1].to_s.gsub(/;$/,""))}" }.join('; ')
185
180
  "#{type}; #{params}"
186
181
  when val =~ /^\s*$/
187
182
  'text/plain'
188
183
  else
189
- ''
184
+ val
190
185
  end
191
186
  end
192
187
 
@@ -37,9 +37,8 @@ module Mail
37
37
  if Utilities.blank?(value)
38
38
  value = ::DateTime.now.strftime('%a, %d %b %Y %H:%M:%S %z')
39
39
  else
40
- value = strip_field(FIELD_NAME, value)
41
- value.to_s.gsub!(/\(.*?\)/, '')
42
- value = ::DateTime.parse(value.to_s.squeeze(" ")).strftime('%a, %d %b %Y %H:%M:%S %z')
40
+ value = value.to_s.gsub(/\(.*?\)/, '').squeeze(' ')
41
+ value = ::DateTime.parse(value).strftime('%a, %d %b %Y %H:%M:%S %z')
43
42
  end
44
43
  super(CAPITALIZED_FIELD, value, charset)
45
44
  rescue ArgumentError => e
@@ -39,7 +39,7 @@ module Mail
39
39
 
40
40
  def initialize(value = nil, charset = 'utf-8')
41
41
  self.charset = charset
42
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
42
+ super(CAPITALIZED_FIELD, value, charset)
43
43
  self
44
44
  end
45
45
 
@@ -40,7 +40,7 @@ module Mail
40
40
  def initialize(value = nil, charset = 'utf-8')
41
41
  self.charset = charset
42
42
  value = value.join("\r\n\s") if value.is_a?(Array)
43
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
43
+ super(CAPITALIZED_FIELD, value, charset)
44
44
  self.parse
45
45
  self
46
46
  end
@@ -10,7 +10,7 @@ module Mail
10
10
 
11
11
  def initialize(value = nil, charset = 'utf-8')
12
12
  self.charset = charset
13
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
13
+ super(CAPITALIZED_FIELD, value, charset)
14
14
  self
15
15
  end
16
16
 
@@ -46,7 +46,7 @@ module Mail
46
46
  self.name = CAPITALIZED_FIELD
47
47
  self.value = generate_message_id
48
48
  else
49
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
49
+ super(CAPITALIZED_FIELD, value, charset)
50
50
  end
51
51
  self.parse
52
52
  self
@@ -14,7 +14,7 @@ module Mail
14
14
  if Utilities.blank?(value)
15
15
  value = '1.0'
16
16
  end
17
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
17
+ super(CAPITALIZED_FIELD, value, charset)
18
18
  self.parse
19
19
  self
20
20
 
@@ -9,6 +9,9 @@ require 'mail/fields/unstructured_field'
9
9
 
10
10
  module Mail
11
11
  class OptionalField < UnstructuredField
12
-
12
+ private
13
+ def do_encode
14
+ "#{wrapped_value}\r\n"
15
+ end
13
16
  end
14
17
  end
@@ -28,7 +28,7 @@ module Mail
28
28
 
29
29
  def initialize(value = nil, charset = 'utf-8')
30
30
  self.charset = charset
31
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
31
+ super(CAPITALIZED_FIELD, value, charset)
32
32
  self.parse
33
33
  self
34
34
 
@@ -40,7 +40,7 @@ module Mail
40
40
  def initialize(value = nil, charset = 'utf-8')
41
41
  self.charset = charset
42
42
  value = value.join("\r\n\s") if value.is_a?(Array)
43
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
43
+ super(CAPITALIZED_FIELD, value, charset)
44
44
  self.parse
45
45
  self
46
46
  end
@@ -39,7 +39,7 @@ module Mail
39
39
 
40
40
  def initialize(value = nil, charset = 'utf-8')
41
41
  self.charset = charset
42
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
42
+ super(CAPITALIZED_FIELD, value, charset)
43
43
  self
44
44
  end
45
45
 
@@ -39,7 +39,7 @@ module Mail
39
39
 
40
40
  def initialize(value = nil, charset = 'utf-8')
41
41
  self.charset = charset
42
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
42
+ super(CAPITALIZED_FIELD, value, charset)
43
43
  self
44
44
  end
45
45
 
@@ -39,7 +39,7 @@ module Mail
39
39
 
40
40
  def initialize(value = nil, charset = 'utf-8')
41
41
  self.charset = charset
42
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
42
+ super(CAPITALIZED_FIELD, value, charset)
43
43
  self
44
44
  end
45
45
 
@@ -17,7 +17,6 @@ module Mail
17
17
  if Utilities.blank?(value)
18
18
  value = ::DateTime.now.strftime('%a, %d %b %Y %H:%M:%S %z')
19
19
  else
20
- value = strip_field(FIELD_NAME, value)
21
20
  value = ::DateTime.parse(value.to_s).strftime('%a, %d %b %Y %H:%M:%S %z')
22
21
  end
23
22
  super(CAPITALIZED_FIELD, value, charset)
@@ -39,7 +39,7 @@ module Mail
39
39
 
40
40
  def initialize(value = nil, charset = 'utf-8')
41
41
  self.charset = charset
42
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
42
+ super(CAPITALIZED_FIELD, value, charset)
43
43
  self
44
44
  end
45
45
 
@@ -14,7 +14,7 @@ module Mail
14
14
 
15
15
  def initialize(value = nil, charset = 'utf-8')
16
16
  self.charset = charset
17
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
17
+ super(CAPITALIZED_FIELD, value, charset)
18
18
  self.parse
19
19
  self
20
20
  end
@@ -38,7 +38,7 @@ module Mail
38
38
 
39
39
  def initialize(value = nil, charset = 'utf-8')
40
40
  self.charset = charset
41
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
41
+ super(CAPITALIZED_FIELD, value, charset)
42
42
  self
43
43
  end
44
44
 
@@ -39,7 +39,7 @@ module Mail
39
39
 
40
40
  def initialize(value = nil, charset = 'utf-8')
41
41
  self.charset = charset
42
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
42
+ super(CAPITALIZED_FIELD, value, charset)
43
43
  self
44
44
  end
45
45
 
@@ -41,7 +41,7 @@ module Mail
41
41
  def initialize(value = nil, charset = 'utf-8')
42
42
  value = nil if value == '<>'
43
43
  self.charset = charset
44
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
44
+ super(CAPITALIZED_FIELD, value, charset)
45
45
  self
46
46
  end
47
47
 
@@ -39,7 +39,7 @@ module Mail
39
39
 
40
40
  def initialize(value = nil, charset = 'utf-8')
41
41
  self.charset = charset
42
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
42
+ super(CAPITALIZED_FIELD, value, charset)
43
43
  self
44
44
  end
45
45
 
@@ -10,7 +10,7 @@ module Mail
10
10
 
11
11
  def initialize(value = nil, charset = 'utf-8')
12
12
  self.charset = charset
13
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
13
+ super(CAPITALIZED_FIELD, value, charset)
14
14
  end
15
15
 
16
16
  end
@@ -39,7 +39,7 @@ module Mail
39
39
 
40
40
  def initialize(value = nil, charset = 'utf-8')
41
41
  self.charset = charset
42
- super(CAPITALIZED_FIELD, strip_field(FIELD_NAME, value), charset)
42
+ super(CAPITALIZED_FIELD, value, charset)
43
43
  self
44
44
  end
45
45
 
@@ -32,6 +32,12 @@ module Mail
32
32
  else
33
33
  # Ensure we are dealing with a string
34
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
35
41
  end
36
42
 
37
43
  if charset
@@ -67,7 +73,11 @@ module Mail
67
73
  private
68
74
 
69
75
  def do_encode
70
- value.nil? ? '' : "#{wrapped_value}\r\n"
76
+ if value && !value.empty?
77
+ "#{wrapped_value}\r\n"
78
+ else
79
+ ''
80
+ end
71
81
  end
72
82
 
73
83
  def do_decode
@@ -121,7 +131,7 @@ module Mail
121
131
  def fold(prepend = 0) # :nodoc:
122
132
  encoding = normalized_encoding
123
133
  decoded_string = decoded.to_s
124
- should_encode = decoded_string.not_ascii_only?
134
+ should_encode = !decoded_string.ascii_only?
125
135
  if should_encode
126
136
  first = true
127
137
  words = decoded_string.split(/[ \t]/).map do |word|
@@ -130,7 +140,7 @@ module Mail
130
140
  else
131
141
  word = " #{word}"
132
142
  end
133
- if word.not_ascii_only?
143
+ if !word.ascii_only?
134
144
  word
135
145
  else
136
146
  word.scan(/.{7}|.+$/)
@@ -148,7 +158,14 @@ module Mail
148
158
  first_word = true
149
159
  while !words.empty?
150
160
  break unless word = words.first.dup
151
- 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
+
152
169
  word = encode(word) if should_encode
153
170
  word = encode_crlf(word)
154
171
  # Skip to next line if we're going to go past the limit
data/lib/mail/header.rb CHANGED
@@ -50,13 +50,14 @@ module Mail
50
50
  # me the example so we can fix it.
51
51
  def initialize(header_text = nil, charset = nil)
52
52
  @charset = charset
53
- self.raw_source = ::Mail::Utilities.to_crlf(header_text).lstrip
53
+ self.raw_source = header_text
54
54
  split_header if header_text
55
55
  end
56
56
 
57
57
  def initialize_copy(original)
58
58
  super
59
59
  @fields = @fields.dup
60
+ @fields.map!(&:dup)
60
61
  end
61
62
 
62
63
  # The preserved raw source of the header as you passed it in, untouched
@@ -92,14 +93,15 @@ module Mail
92
93
  # h.fields = ['From: mikel@me.com', 'To: bob@you.com']
93
94
  def fields=(unfolded_fields)
94
95
  @fields = Mail::FieldList.new
95
- warn "Warning: more than #{self.class.maximum_amount} header fields only using the first #{self.class.maximum_amount}" if unfolded_fields.length > self.class.maximum_amount
96
+ 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
96
97
  unfolded_fields[0..(self.class.maximum_amount-1)].each do |field|
97
98
 
98
- field = Field.new(field, nil, charset)
99
- if limited_field?(field.name) && (selected = select_field_for(field.name)) && selected.any?
100
- selected.first.update(field.name, field.value)
101
- else
102
- @fields << field
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
103
105
  end
104
106
  end
105
107
 
@@ -248,7 +250,7 @@ module Mail
248
250
  private
249
251
 
250
252
  def raw_source=(val)
251
- @raw_source = val
253
+ @raw_source = ::Mail::Utilities.to_crlf(val).lstrip
252
254
  end
253
255
 
254
256
  # Splits an unfolded and line break cleaned header into individual field
data/lib/mail/mail.rb CHANGED
@@ -90,19 +90,11 @@ module Mail
90
90
  # Each mail object inherits the default set in Mail.delivery_method, however, on
91
91
  # a per email basis, you can override the method:
92
92
  #
93
- # mail.delivery_method :sendmail
93
+ # mail.delivery_method :smtp
94
94
  #
95
95
  # Or you can override the method and pass in settings:
96
96
  #
97
- # mail.delivery_method :sendmail, { :address => 'some.host' }
98
- #
99
- # You can also just modify the settings:
100
- #
101
- # mail.delivery_settings = { :address => 'some.host' }
102
- #
103
- # The passed in hash is just merged against the defaults with +merge!+ and the result
104
- # assigned the mail object. So the above example will change only the :address value
105
- # of the global smtp_settings to be 'some.host', keeping all other values
97
+ # mail.delivery_method :smtp, :address => 'some.host'
106
98
  def self.defaults(&block)
107
99
  Configuration.instance.instance_eval(&block)
108
100
  end
@@ -83,6 +83,16 @@ module Mail
83
83
  self
84
84
  end
85
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
+
86
96
  def description
87
97
  result = "send a matching email"
88
98
  result
@@ -108,7 +118,7 @@ module Mail
108
118
  candidate_deliveries = deliveries
109
119
  modifiers =
110
120
  %w(sender recipients copy_recipients blind_copy_recipients subject
111
- subject_matcher body body_matcher having_attachments attachments)
121
+ subject_matcher body body_matcher html_part_body text_part_body having_attachments attachments)
112
122
  modifiers.each do |modifier_name|
113
123
  next unless instance_variable_defined?("@#{modifier_name}")
114
124
  candidate_deliveries = candidate_deliveries.select{|matching_delivery| self.send("matches_on_#{modifier_name}?", matching_delivery)}
@@ -160,6 +170,14 @@ module Mail
160
170
  @body_matcher.match delivery.body.raw_source
161
171
  end
162
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
+
163
181
  def explain_expectations
164
182
  result = ''
165
183
  result += "from #{@sender} " if instance_variable_defined?('@sender')
@@ -170,6 +188,8 @@ module Mail
170
188
  result += "with subject matching \"#{@subject_matcher}\" " if instance_variable_defined?('@subject_matcher')
171
189
  result += "with body \"#{@body}\" " if instance_variable_defined?('@body')
172
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')
173
193
  result
174
194
  end
175
195