mail 2.5.5 → 2.8.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 (191) hide show
  1. checksums.yaml +5 -5
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +170 -108
  4. data/lib/mail/attachments_list.rb +13 -10
  5. data/lib/mail/body.rb +105 -91
  6. data/lib/mail/check_delivery_params.rb +30 -22
  7. data/lib/mail/configuration.rb +3 -0
  8. data/lib/mail/constants.rb +79 -0
  9. data/lib/mail/elements/address.rb +118 -174
  10. data/lib/mail/elements/address_list.rb +16 -56
  11. data/lib/mail/elements/content_disposition_element.rb +12 -22
  12. data/lib/mail/elements/content_location_element.rb +9 -17
  13. data/lib/mail/elements/content_transfer_encoding_element.rb +8 -19
  14. data/lib/mail/elements/content_type_element.rb +20 -30
  15. data/lib/mail/elements/date_time_element.rb +10 -21
  16. data/lib/mail/elements/envelope_from_element.rb +23 -31
  17. data/lib/mail/elements/message_ids_element.rb +22 -20
  18. data/lib/mail/elements/mime_version_element.rb +10 -21
  19. data/lib/mail/elements/phrase_list.rb +13 -15
  20. data/lib/mail/elements/received_element.rb +26 -21
  21. data/lib/mail/elements.rb +1 -0
  22. data/lib/mail/encodings/7bit.rb +10 -14
  23. data/lib/mail/encodings/8bit.rb +5 -18
  24. data/lib/mail/encodings/base64.rb +15 -10
  25. data/lib/mail/encodings/binary.rb +4 -22
  26. data/lib/mail/encodings/identity.rb +24 -0
  27. data/lib/mail/encodings/quoted_printable.rb +13 -7
  28. data/lib/mail/encodings/transfer_encoding.rb +47 -28
  29. data/lib/mail/encodings/unix_to_unix.rb +20 -0
  30. data/lib/mail/encodings.rb +102 -93
  31. data/lib/mail/envelope.rb +12 -19
  32. data/lib/mail/field.rb +143 -71
  33. data/lib/mail/field_list.rb +73 -19
  34. data/lib/mail/fields/bcc_field.rb +42 -48
  35. data/lib/mail/fields/cc_field.rb +29 -50
  36. data/lib/mail/fields/comments_field.rb +28 -37
  37. data/lib/mail/fields/common_address_field.rb +170 -0
  38. data/lib/mail/fields/common_date_field.rb +58 -0
  39. data/lib/mail/fields/common_field.rb +77 -0
  40. data/lib/mail/fields/common_message_id_field.rb +42 -0
  41. data/lib/mail/fields/content_description_field.rb +8 -14
  42. data/lib/mail/fields/content_disposition_field.rb +20 -44
  43. data/lib/mail/fields/content_id_field.rb +25 -51
  44. data/lib/mail/fields/content_location_field.rb +12 -25
  45. data/lib/mail/fields/content_transfer_encoding_field.rb +31 -36
  46. data/lib/mail/fields/content_type_field.rb +51 -80
  47. data/lib/mail/fields/date_field.rb +24 -52
  48. data/lib/mail/fields/from_field.rb +29 -50
  49. data/lib/mail/fields/in_reply_to_field.rb +39 -49
  50. data/lib/mail/fields/keywords_field.rb +19 -32
  51. data/lib/mail/fields/message_id_field.rb +26 -71
  52. data/lib/mail/fields/mime_version_field.rb +20 -30
  53. data/lib/mail/fields/named_structured_field.rb +11 -0
  54. data/lib/mail/fields/named_unstructured_field.rb +11 -0
  55. data/lib/mail/fields/optional_field.rb +10 -7
  56. data/lib/mail/fields/{common/parameter_hash.rb → parameter_hash.rb} +16 -13
  57. data/lib/mail/fields/received_field.rb +44 -57
  58. data/lib/mail/fields/references_field.rb +36 -49
  59. data/lib/mail/fields/reply_to_field.rb +29 -50
  60. data/lib/mail/fields/resent_bcc_field.rb +29 -50
  61. data/lib/mail/fields/resent_cc_field.rb +29 -50
  62. data/lib/mail/fields/resent_date_field.rb +6 -30
  63. data/lib/mail/fields/resent_from_field.rb +29 -50
  64. data/lib/mail/fields/resent_message_id_field.rb +6 -29
  65. data/lib/mail/fields/resent_sender_field.rb +28 -57
  66. data/lib/mail/fields/resent_to_field.rb +29 -50
  67. data/lib/mail/fields/return_path_field.rb +51 -55
  68. data/lib/mail/fields/sender_field.rb +35 -56
  69. data/lib/mail/fields/structured_field.rb +4 -30
  70. data/lib/mail/fields/subject_field.rb +10 -11
  71. data/lib/mail/fields/to_field.rb +29 -50
  72. data/lib/mail/fields/unstructured_field.rb +43 -51
  73. data/lib/mail/fields.rb +1 -0
  74. data/lib/mail/header.rb +78 -129
  75. data/lib/mail/indifferent_hash.rb +1 -0
  76. data/lib/mail/mail.rb +18 -11
  77. data/lib/mail/matchers/attachment_matchers.rb +44 -0
  78. data/lib/mail/matchers/has_sent_mail.rb +81 -4
  79. data/lib/mail/message.rb +142 -139
  80. data/lib/mail/multibyte/chars.rb +24 -180
  81. data/lib/mail/multibyte/unicode.rb +32 -27
  82. data/lib/mail/multibyte/utils.rb +27 -43
  83. data/lib/mail/multibyte.rb +56 -16
  84. data/lib/mail/network/delivery_methods/exim.rb +6 -4
  85. data/lib/mail/network/delivery_methods/file_delivery.rb +12 -10
  86. data/lib/mail/network/delivery_methods/logger_delivery.rb +34 -0
  87. data/lib/mail/network/delivery_methods/sendmail.rb +63 -21
  88. data/lib/mail/network/delivery_methods/smtp.rb +76 -50
  89. data/lib/mail/network/delivery_methods/smtp_connection.rb +4 -4
  90. data/lib/mail/network/delivery_methods/test_mailer.rb +5 -2
  91. data/lib/mail/network/retriever_methods/base.rb +9 -8
  92. data/lib/mail/network/retriever_methods/imap.rb +37 -18
  93. data/lib/mail/network/retriever_methods/pop3.rb +6 -3
  94. data/lib/mail/network/retriever_methods/test_retriever.rb +4 -2
  95. data/lib/mail/network.rb +2 -0
  96. data/lib/mail/parser_tools.rb +15 -0
  97. data/lib/mail/parsers/address_lists_parser.rb +33242 -0
  98. data/lib/mail/parsers/address_lists_parser.rl +179 -0
  99. data/lib/mail/parsers/content_disposition_parser.rb +901 -0
  100. data/lib/mail/parsers/content_disposition_parser.rl +89 -0
  101. data/lib/mail/parsers/content_location_parser.rb +822 -0
  102. data/lib/mail/parsers/content_location_parser.rl +78 -0
  103. data/lib/mail/parsers/content_transfer_encoding_parser.rb +522 -0
  104. data/lib/mail/parsers/content_transfer_encoding_parser.rl +71 -0
  105. data/lib/mail/parsers/content_type_parser.rb +1048 -0
  106. data/lib/mail/parsers/content_type_parser.rl +90 -0
  107. data/lib/mail/parsers/date_time_parser.rb +891 -0
  108. data/lib/mail/parsers/date_time_parser.rl +69 -0
  109. data/lib/mail/parsers/envelope_from_parser.rb +3675 -0
  110. data/lib/mail/parsers/envelope_from_parser.rl +89 -0
  111. data/lib/mail/parsers/message_ids_parser.rb +5161 -0
  112. data/lib/mail/parsers/message_ids_parser.rl +93 -0
  113. data/lib/mail/parsers/mime_version_parser.rb +513 -0
  114. data/lib/mail/parsers/mime_version_parser.rl +68 -0
  115. data/lib/mail/parsers/phrase_lists_parser.rb +884 -0
  116. data/lib/mail/parsers/phrase_lists_parser.rl +90 -0
  117. data/lib/mail/parsers/received_parser.rb +8782 -0
  118. data/lib/mail/parsers/received_parser.rl +91 -0
  119. data/lib/mail/parsers/rfc2045_content_transfer_encoding.rl +13 -0
  120. data/lib/mail/parsers/rfc2045_content_type.rl +25 -0
  121. data/lib/mail/parsers/rfc2045_mime.rl +16 -0
  122. data/lib/mail/parsers/rfc2183_content_disposition.rl +15 -0
  123. data/lib/mail/parsers/rfc3629_utf8.rl +19 -0
  124. data/lib/mail/parsers/rfc5234_abnf_core_rules.rl +22 -0
  125. data/lib/mail/parsers/rfc5322.rl +74 -0
  126. data/lib/mail/parsers/rfc5322_address.rl +72 -0
  127. data/lib/mail/parsers/rfc5322_date_time.rl +37 -0
  128. data/lib/mail/parsers/rfc5322_lexical_tokens.rl +60 -0
  129. data/lib/mail/parsers.rb +13 -0
  130. data/lib/mail/part.rb +11 -12
  131. data/lib/mail/parts_list.rb +90 -14
  132. data/lib/mail/smtp_envelope.rb +57 -0
  133. data/lib/mail/utilities.rb +415 -76
  134. data/lib/mail/values/unicode_tables.dat +0 -0
  135. data/lib/mail/version.rb +8 -15
  136. data/lib/mail/yaml.rb +30 -0
  137. data/lib/mail.rb +9 -32
  138. metadata +127 -79
  139. data/CHANGELOG.rdoc +0 -742
  140. data/CONTRIBUTING.md +0 -45
  141. data/Dependencies.txt +0 -3
  142. data/Gemfile +0 -32
  143. data/Rakefile +0 -21
  144. data/TODO.rdoc +0 -9
  145. data/lib/VERSION +0 -4
  146. data/lib/load_parsers.rb +0 -35
  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/smtp.rb +0 -24
  150. data/lib/mail/core_extensions/string/access.rb +0 -145
  151. data/lib/mail/core_extensions/string/multibyte.rb +0 -78
  152. data/lib/mail/core_extensions/string.rb +0 -33
  153. data/lib/mail/fields/common/address_container.rb +0 -16
  154. data/lib/mail/fields/common/common_address.rb +0 -140
  155. data/lib/mail/fields/common/common_date.rb +0 -42
  156. data/lib/mail/fields/common/common_field.rb +0 -57
  157. data/lib/mail/fields/common/common_message_id.rb +0 -48
  158. data/lib/mail/multibyte/exceptions.rb +0 -8
  159. data/lib/mail/parsers/address_lists.rb +0 -64
  160. data/lib/mail/parsers/address_lists.treetop +0 -19
  161. data/lib/mail/parsers/content_disposition.rb +0 -535
  162. data/lib/mail/parsers/content_disposition.treetop +0 -46
  163. data/lib/mail/parsers/content_location.rb +0 -139
  164. data/lib/mail/parsers/content_location.treetop +0 -20
  165. data/lib/mail/parsers/content_transfer_encoding.rb +0 -201
  166. data/lib/mail/parsers/content_transfer_encoding.treetop +0 -18
  167. data/lib/mail/parsers/content_type.rb +0 -971
  168. data/lib/mail/parsers/content_type.treetop +0 -68
  169. data/lib/mail/parsers/date_time.rb +0 -114
  170. data/lib/mail/parsers/date_time.treetop +0 -11
  171. data/lib/mail/parsers/envelope_from.rb +0 -194
  172. data/lib/mail/parsers/envelope_from.treetop +0 -32
  173. data/lib/mail/parsers/message_ids.rb +0 -45
  174. data/lib/mail/parsers/message_ids.treetop +0 -15
  175. data/lib/mail/parsers/mime_version.rb +0 -144
  176. data/lib/mail/parsers/mime_version.treetop +0 -19
  177. data/lib/mail/parsers/phrase_lists.rb +0 -45
  178. data/lib/mail/parsers/phrase_lists.treetop +0 -15
  179. data/lib/mail/parsers/received.rb +0 -71
  180. data/lib/mail/parsers/received.treetop +0 -11
  181. data/lib/mail/parsers/rfc2045.rb +0 -421
  182. data/lib/mail/parsers/rfc2045.treetop +0 -35
  183. data/lib/mail/parsers/rfc2822.rb +0 -5397
  184. data/lib/mail/parsers/rfc2822.treetop +0 -408
  185. data/lib/mail/parsers/rfc2822_obsolete.rb +0 -3768
  186. data/lib/mail/parsers/rfc2822_obsolete.treetop +0 -241
  187. data/lib/mail/patterns.rb +0 -35
  188. data/lib/mail/version_specific/ruby_1_8.rb +0 -119
  189. data/lib/mail/version_specific/ruby_1_9.rb +0 -147
  190. data/lib/tasks/corpus.rake +0 -125
  191. data/lib/tasks/treetop.rake +0 -10
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
  module Mail
3
4
  module Encodings
4
5
  class TransferEncoding
@@ -6,52 +7,70 @@ module Mail
6
7
 
7
8
  PRIORITY = -1
8
9
 
10
+ # And encoding's superclass can always transport it since the
11
+ # class hierarchy is arranged e.g. Base64 < 7bit < 8bit < Binary.
9
12
  def self.can_transport?(enc)
10
- enc = Encodings.get_name(enc)
11
- if Encodings.defined? enc
12
- Encodings.get_encoding(enc).new.is_a? self
13
- else
14
- false
15
- end
13
+ enc && enc <= self
16
14
  end
17
15
 
16
+ # Override in subclasses to indicate that they can encode text
17
+ # that couldn't be directly transported, e.g. Base64 has 7bit output,
18
+ # but it can encode binary.
18
19
  def self.can_encode?(enc)
19
- can_transport? enc
20
+ can_transport? enc
20
21
  end
21
22
 
22
23
  def self.cost(str)
23
24
  raise "Unimplemented"
24
25
  end
25
26
 
27
+ def self.compatible_input?(str)
28
+ true
29
+ end
30
+
26
31
  def self.to_s
27
32
  self::NAME
28
33
  end
29
34
 
30
- def self.get_best_compatible(source_encoding, str)
31
- if self.can_transport? source_encoding then
32
- source_encoding
35
+ def self.negotiate(message_encoding, source_encoding, str, allowed_encodings = nil)
36
+ message_encoding = Encodings.get_encoding(message_encoding) || Encodings.get_encoding('8bit')
37
+ source_encoding = Encodings.get_encoding(source_encoding)
38
+
39
+ if message_encoding && source_encoding && message_encoding.can_transport?(source_encoding) && source_encoding.compatible_input?(str)
40
+ source_encoding
33
41
  else
34
- choices = []
35
- Encodings.get_all.each do |enc|
36
- choices << enc if self.can_transport? enc and enc.can_encode? source_encoding
37
- end
38
- best = nil
39
- best_cost = 100
40
- choices.each do |enc|
41
- this_cost = enc.cost str
42
- if this_cost < best_cost then
43
- best_cost = this_cost
44
- best = enc
45
- elsif this_cost == best_cost then
46
- best = enc if enc::PRIORITY < best::PRIORITY
47
- end
48
- end
49
- best
42
+ renegotiate(message_encoding, source_encoding, str, allowed_encodings)
50
43
  end
51
44
  end
52
45
 
53
- def to_s
54
- self.class.to_s
46
+ def self.renegotiate(message_encoding, source_encoding, str, allowed_encodings = nil)
47
+ encodings = Encodings.get_all.select do |enc|
48
+ (allowed_encodings.nil? || allowed_encodings.include?(enc)) &&
49
+ message_encoding.can_transport?(enc) &&
50
+ enc.can_encode?(source_encoding)
51
+ end
52
+
53
+ lowest_cost(str, encodings)
54
+ end
55
+
56
+ def self.lowest_cost(str, encodings)
57
+ best = nil
58
+ best_cost = nil
59
+
60
+ encodings.each do |enc|
61
+ # If the current choice cannot be transported safely, give priority
62
+ # to other choices but allow it to be used as a fallback.
63
+ this_cost = enc.cost(str) if enc.compatible_input?(str)
64
+
65
+ if !best_cost || (this_cost && this_cost < best_cost)
66
+ best_cost = this_cost
67
+ best = enc
68
+ elsif this_cost == best_cost
69
+ best = enc if enc::PRIORITY < best::PRIORITY
70
+ end
71
+ end
72
+
73
+ best
55
74
  end
56
75
  end
57
76
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+ module Mail
3
+ module Encodings
4
+ class UnixToUnix < TransferEncoding
5
+ NAME = "x-uuencode"
6
+
7
+ def self.decode(str)
8
+ str.sub(/\Abegin \d+ [^\n]*\n/, '').unpack('u').first
9
+ end
10
+
11
+ def self.encode(str)
12
+ [str].pack("u")
13
+ end
14
+
15
+ Encodings.register(NAME, self)
16
+ Encodings.register("uuencode", self)
17
+ Encodings.register("x-uue", self)
18
+ end
19
+ end
20
+ end
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module Mail
4
5
  # Raised when attempting to decode an unknown encoding type
@@ -6,8 +7,7 @@ module Mail
6
7
  end
7
8
 
8
9
  module Encodings
9
-
10
- include Mail::Patterns
10
+ include Mail::Constants
11
11
  extend Mail::Utilities
12
12
 
13
13
  @transfer_encodings = {}
@@ -18,7 +18,7 @@ module Mail
18
18
  #
19
19
  # Encodings.register "base64", Mail::Encodings::Base64
20
20
  def Encodings.register(name, cls)
21
- @transfer_encodings[get_name(name)] = cls
21
+ @transfer_encodings[get_name(name)] = cls
22
22
  end
23
23
 
24
24
  # Is the encoding we want defined?
@@ -26,8 +26,8 @@ module Mail
26
26
  # Example:
27
27
  #
28
28
  # Encodings.defined?(:base64) #=> true
29
- def Encodings.defined?( str )
30
- @transfer_encodings.include? get_name(str)
29
+ def Encodings.defined?(name)
30
+ @transfer_encodings.include? get_name(name)
31
31
  end
32
32
 
33
33
  # Gets a defined encoding type, QuotedPrintable or Base64 for now.
@@ -38,16 +38,24 @@ module Mail
38
38
  # Example:
39
39
  #
40
40
  # Encodings.get_encoding(:base64) #=> Mail::Encodings::Base64
41
- def Encodings.get_encoding( str )
42
- @transfer_encodings[get_name(str)]
41
+ def Encodings.get_encoding(name)
42
+ @transfer_encodings[get_name(name)]
43
43
  end
44
44
 
45
45
  def Encodings.get_all
46
46
  @transfer_encodings.values
47
47
  end
48
48
 
49
- def Encodings.get_name(enc)
50
- enc = enc.to_s.gsub("-", "_").downcase
49
+ def Encodings.get_name(name)
50
+ underscoreize(name).downcase
51
+ end
52
+
53
+ def Encodings.transcode_charset(str, from_charset, to_charset = 'UTF-8')
54
+ if from_charset
55
+ Utilities.transcode_charset str, from_charset, to_charset
56
+ else
57
+ str
58
+ end
51
59
  end
52
60
 
53
61
  # Encodes a parameter value using URI Escaping, note the language field 'en' can
@@ -57,8 +65,7 @@ module Mail
57
65
  # param_encode_language 'jp'
58
66
  # end
59
67
  #
60
- # The character set used for encoding will either be the value of $KCODE for
61
- # Ruby < 1.9 or the encoding on the string passed in.
68
+ # The character set used for encoding will be the encoding on the string passed in.
62
69
  #
63
70
  # Example:
64
71
  #
@@ -70,7 +77,7 @@ module Mail
70
77
  when str.ascii_only?
71
78
  str
72
79
  else
73
- RubyVer.param_encode(str)
80
+ Utilities.param_encode(str)
74
81
  end
75
82
  end
76
83
 
@@ -84,15 +91,15 @@ module Mail
84
91
  # str.encoding #=> 'ISO-8859-1' ## Only on Ruby 1.9
85
92
  # str #=> "This is fun"
86
93
  def Encodings.param_decode(str, encoding)
87
- RubyVer.param_decode(str, encoding)
94
+ Utilities.param_decode(str, encoding)
88
95
  end
89
96
 
90
97
  # Decodes or encodes a string as needed for either Base64 or QP encoding types in
91
98
  # the =?<encoding>?[QB]?<string>?=" format.
92
99
  #
93
100
  # The output type needs to be :decode to decode the input string or :encode to
94
- # encode the input string. The character set used for encoding will either be
95
- # the value of $KCODE for Ruby < 1.9 or the encoding on the string passed in.
101
+ # encode the input string. The character set used for encoding will be the
102
+ # encoding on the string passed in.
96
103
  #
97
104
  # On encoding, will only send out Base64 encoded strings.
98
105
  def Encodings.decode_encode(str, output_type)
@@ -103,7 +110,7 @@ module Mail
103
110
  if str.ascii_only?
104
111
  str
105
112
  else
106
- Encodings.b_value_encode(str, find_encoding(str))
113
+ Encodings.b_value_encode(str, str.encoding)
107
114
  end
108
115
  end
109
116
  end
@@ -114,34 +121,19 @@ module Mail
114
121
  # String has to be of the format =?<encoding>?[QB]?<string>?=
115
122
  def Encodings.value_decode(str)
116
123
  # Optimization: If there's no encoded-words in the string, just return it
117
- return str unless str =~ /\=\?[^?]+\?[QB]\?[^?]+?\?\=/xmi
124
+ return str unless str =~ ENCODED_VALUE
118
125
 
119
126
  lines = collapse_adjacent_encodings(str)
120
127
 
121
128
  # Split on white-space boundaries with capture, so we capture the white-space as well
122
- lines.map do |line|
123
- line.split(/([ \t])/).map do |text|
124
- if text.index('=?').nil?
125
- text
126
- else
127
- # Search for occurences of quoted strings or plain strings
128
- text.scan(/( # Group around entire regex to include it in matches
129
- \=\?[^?]+\?([QB])\?[^?]+?\?\= # Quoted String with subgroup for encoding method
130
- | # or
131
- .+?(?=\=\?|$) # Plain String
132
- )/xmi).map do |matches|
133
- string, method = *matches
134
- if method == 'b' || method == 'B'
135
- b_value_decode(string)
136
- elsif method == 'q' || method == 'Q'
137
- q_value_decode(string)
138
- else
139
- string
140
- end
141
- end
129
+ lines.each do |line|
130
+ line.gsub!(ENCODED_VALUE) do |string|
131
+ case $2
132
+ when *B_VALUES then b_value_decode(string)
133
+ when *Q_VALUES then q_value_decode(string)
142
134
  end
143
135
  end
144
- end.flatten.join("")
136
+ end.join("")
145
137
  end
146
138
 
147
139
  # Takes an encoded string of the format =?<encoding>?[QB]?<string>?=
@@ -152,13 +144,8 @@ module Mail
152
144
  output
153
145
  elsif to_encoding
154
146
  begin
155
- if RUBY_VERSION >= '1.9'
156
- output.encode(to_encoding)
157
- else
158
- require 'iconv'
159
- Iconv.iconv(to_encoding, 'UTF-8', output).first
160
- end
161
- rescue Iconv::IllegalSequence, Iconv::InvalidEncoding, Errno::EINVAL
147
+ output.encode(to_encoding)
148
+ rescue Errno::EINVAL
162
149
  # the 'from' parameter specifies a charset other than what the text
163
150
  # actually is...not much we can do in this case but just return the
164
151
  # unconverted text.
@@ -174,21 +161,21 @@ module Mail
174
161
 
175
162
  def Encodings.address_encode(address, charset = 'utf-8')
176
163
  if address.is_a?(Array)
177
- # loop back through for each element
178
164
  address.compact.map { |a| Encodings.address_encode(a, charset) }.join(", ")
179
- else
180
- # find any word boundary that is not ascii and encode it
181
- encode_non_usascii(address, charset) if address
165
+ elsif address
166
+ encode_non_usascii(address, charset)
182
167
  end
183
168
  end
184
169
 
185
170
  def Encodings.encode_non_usascii(address, charset)
186
171
  return address if address.ascii_only? or charset.nil?
187
- us_ascii = %Q{\x00-\x7f}
188
- # Encode any non usascii strings embedded inside of quotes
189
- address = address.gsub(/(".*?[^#{us_ascii}].*?")/) { |s| Encodings.b_value_encode(unquote(s), charset) }
172
+
173
+ # Encode all strings embedded inside of quotes
174
+ address = address.gsub(/("[^"]*[^\/]")/) { |s| Encodings.b_value_encode(unquote(s), charset) }
175
+
190
176
  # Then loop through all remaining items and encode as needed
191
177
  tokens = address.split(/\s/)
178
+
192
179
  map_with_index(tokens) do |word, i|
193
180
  if word.ascii_only?
194
181
  word
@@ -209,12 +196,15 @@ module Mail
209
196
  #
210
197
  # Encodings.b_value_encode('This is あ string', 'UTF-8')
211
198
  # #=> "=?UTF-8?B?VGhpcyBpcyDjgYIgc3RyaW5n?="
212
- def Encodings.b_value_encode(encoded_str, encoding = nil)
213
- return encoded_str if encoded_str.to_s.ascii_only?
214
- string, encoding = RubyVer.b_value_encode(encoded_str, encoding)
215
- map_lines(string) do |str|
216
- "=?#{encoding}?B?#{str.chomp}?="
217
- end.join(" ")
199
+ def Encodings.b_value_encode(string, encoding = nil)
200
+ if string.to_s.ascii_only?
201
+ string
202
+ else
203
+ Encodings.each_base64_chunk_byterange(string, 60).map do |chunk|
204
+ str, encoding = Utilities.b_value_encode(chunk, encoding)
205
+ "=?#{encoding}?B?#{str.chomp}?="
206
+ end.join(" ")
207
+ end
218
208
  end
219
209
 
220
210
  # Encode a string with Quoted-Printable Encoding and returns it ready to be inserted
@@ -226,7 +216,7 @@ module Mail
226
216
  # #=> "=?UTF-8?Q?This_is_=E3=81=82_string?="
227
217
  def Encodings.q_value_encode(encoded_str, encoding = nil)
228
218
  return encoded_str if encoded_str.to_s.ascii_only?
229
- string, encoding = RubyVer.q_value_encode(encoded_str, encoding)
219
+ string, encoding = Utilities.q_value_encode(encoded_str, encoding)
230
220
  string.gsub!("=\r\n", '') # We already have limited the string to the length we want
231
221
  map_lines(string) do |str|
232
222
  "=?#{encoding}?Q?#{str.chomp.gsub(/ /, '_')}?="
@@ -242,7 +232,7 @@ module Mail
242
232
  # Encodings.b_value_decode("=?UTF-8?B?VGhpcyBpcyDjgYIgc3RyaW5n?=")
243
233
  # #=> 'This is あ string'
244
234
  def Encodings.b_value_decode(str)
245
- RubyVer.b_value_decode(str)
235
+ Utilities.b_value_decode(str)
246
236
  end
247
237
 
248
238
  # Decodes a Quoted-Printable string from the "=?UTF-8?Q?This_is_=E3=81=82_string?=" format
@@ -252,54 +242,73 @@ module Mail
252
242
  # Encodings.q_value_decode("=?UTF-8?Q?This_is_=E3=81=82_string?=")
253
243
  # #=> 'This is あ string'
254
244
  def Encodings.q_value_decode(str)
255
- RubyVer.q_value_decode(str)
256
- end
257
-
258
- def Encodings.split_encoding_from_string( str )
259
- match = str.match(/\=\?([^?]+)?\?[QB]\?(.+)?\?\=/mi)
260
- if match
261
- match[1]
262
- else
263
- nil
264
- end
265
- end
266
-
267
- def Encodings.find_encoding(str)
268
- RUBY_VERSION >= '1.9' ? str.encoding : $KCODE
245
+ Utilities.q_value_decode(str)
269
246
  end
270
247
 
271
248
  # Gets the encoding type (Q or B) from the string.
272
- def Encodings.split_value_encoding_from_string(str)
273
- match = str.match(/\=\?[^?]+?\?([QB])\?(.+)?\?\=/mi)
274
- if match
275
- match[1]
276
- else
277
- nil
278
- end
249
+ def Encodings.value_encoding_from_string(str)
250
+ str[ENCODED_VALUE, 1]
279
251
  end
280
252
 
281
- # When the encoded string consists of multiple lines, lines with the same
282
- # encoding (Q or B) can be joined together.
253
+ # Split header line into proper encoded and unencoded parts.
283
254
  #
284
255
  # String has to be of the format =?<encoding>?[QB]?<string>?=
256
+ #
257
+ # Omit unencoded space after an encoded-word.
285
258
  def Encodings.collapse_adjacent_encodings(str)
286
- lines = str.split(/(\?=)\s*(=\?)/).each_slice(2).map(&:join)
287
259
  results = []
288
- previous_encoding = nil
260
+ last_encoded = nil # Track whether to preserve or drop whitespace
289
261
 
290
- lines.each do |line|
291
- encoding = split_value_encoding_from_string(line)
262
+ lines = str.split(FULL_ENCODED_VALUE)
263
+ lines.each_slice(2) do |unencoded, encoded|
264
+ if last_encoded = encoded
265
+ if !Utilities.blank?(unencoded) || (!last_encoded && unencoded != EMPTY)
266
+ results << unencoded
267
+ end
292
268
 
293
- if encoding == previous_encoding
294
- line = results.pop + line
295
- line.gsub!(/\?\=\=\?.+?\?[QqBb]\?/m, '')
269
+ results << encoded
270
+ else
271
+ results << unencoded
296
272
  end
297
-
298
- previous_encoding = encoding
299
- results << line
300
273
  end
301
274
 
302
275
  results
303
276
  end
277
+
278
+ # Partition the string into bounded-size chunks without splitting
279
+ # multibyte characters.
280
+ def Encodings.each_base64_chunk_byterange(str, max_bytesize_per_base64_chunk, &block)
281
+ raise "size per chunk must be multiple of 4" if (max_bytesize_per_base64_chunk % 4).nonzero?
282
+
283
+ if block_given?
284
+ max_bytesize = ((3 * max_bytesize_per_base64_chunk) / 4.0).floor
285
+ each_chunk_byterange(str, max_bytesize, &block)
286
+ else
287
+ enum_for :each_base64_chunk_byterange, str, max_bytesize_per_base64_chunk
288
+ end
289
+ end
290
+
291
+ # Partition the string into bounded-size chunks without splitting
292
+ # multibyte characters.
293
+ def Encodings.each_chunk_byterange(str, max_bytesize_per_chunk)
294
+ return enum_for(:each_chunk_byterange, str, max_bytesize_per_chunk) unless block_given?
295
+
296
+ offset = 0
297
+ chunksize = 0
298
+
299
+ str.each_char do |chr|
300
+ charsize = chr.bytesize
301
+
302
+ if chunksize + charsize > max_bytesize_per_chunk
303
+ yield Utilities.string_byteslice(str, offset, chunksize)
304
+ offset += chunksize
305
+ chunksize = charsize
306
+ else
307
+ chunksize += charsize
308
+ end
309
+ end
310
+
311
+ yield Utilities.string_byteslice(str, offset, chunksize)
312
+ end
304
313
  end
305
314
  end
data/lib/mail/envelope.rb CHANGED
@@ -1,35 +1,28 @@
1
1
  # encoding: utf-8
2
- #
2
+ # frozen_string_literal: true
3
+ #
3
4
  # = Mail Envelope
4
- #
5
+ #
5
6
  # The Envelope class provides a field for the first line in an
6
7
  # mbox file, that looks like "From mikel@test.lindsaar.net DATETIME"
7
- #
8
+ #
8
9
  # This envelope class reads that line, and turns it into an
9
10
  # Envelope.from and Envelope.date for your use.
11
+
10
12
  module Mail
11
- class Envelope < StructuredField
12
-
13
- def initialize(*args)
14
- super(FIELD_NAME, strip_field(FIELD_NAME, args.last))
15
- end
16
-
17
- def tree
18
- @element ||= Mail::EnvelopeFromElement.new(value)
19
- @tree ||= @element.tree
20
- end
21
-
13
+ class Envelope < NamedStructuredField
14
+ NAME = 'Envelope-From'
15
+
22
16
  def element
23
17
  @element ||= Mail::EnvelopeFromElement.new(value)
24
18
  end
25
-
26
- def date
27
- ::DateTime.parse("#{element.date_time}")
28
- end
29
19
 
30
20
  def from
31
21
  element.address
32
22
  end
33
-
23
+
24
+ def date
25
+ element.date_time
26
+ end
34
27
  end
35
28
  end