mail 2.6.6 → 2.7.1

Sign up to get free protection for your applications and to get access to all the features.
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