mail 2.5.5 → 2.8.1

Sign up to get free protection for your applications and to get access to all the features.
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,6 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
3
+ require 'mail/multibyte/unicode'
2
4
 
3
5
  module Mail #:nodoc:
4
6
  module Multibyte #:nodoc:
@@ -36,16 +38,10 @@ module Mail #:nodoc:
36
38
  alias to_s wrapped_string
37
39
  alias to_str wrapped_string
38
40
 
39
- if RUBY_VERSION >= "1.9"
40
- # Creates a new Chars instance by wrapping _string_.
41
- def initialize(string)
42
- @wrapped_string = string
43
- @wrapped_string.force_encoding(Encoding::UTF_8) unless @wrapped_string.frozen?
44
- end
45
- else
46
- def initialize(string) #:nodoc:
47
- @wrapped_string = string
48
- end
41
+ # Creates a new Chars instance by wrapping _string_.
42
+ def initialize(string)
43
+ @wrapped_string = string.dup
44
+ @wrapped_string.force_encoding(Encoding::UTF_8) unless @wrapped_string.frozen?
49
45
  end
50
46
 
51
47
  # Forward all undefined methods to the wrapped string.
@@ -70,15 +66,6 @@ module Mail #:nodoc:
70
66
  true
71
67
  end
72
68
 
73
- # Returns +true+ when the proxy class can handle the string. Returns +false+ otherwise.
74
- def self.consumes?(string)
75
- # Unpack is a little bit faster than regular expressions.
76
- string.unpack('U*')
77
- true
78
- rescue ArgumentError
79
- false
80
- end
81
-
82
69
  include Comparable
83
70
 
84
71
  # Returns -1, 0, or 1, depending on whether the Chars object is to be sorted before,
@@ -92,158 +79,15 @@ module Mail #:nodoc:
92
79
  @wrapped_string <=> other.to_s
93
80
  end
94
81
 
95
- if RUBY_VERSION < "1.9"
96
- # Returns +true+ if the Chars class can and should act as a proxy for the string _string_. Returns
97
- # +false+ otherwise.
98
- def self.wants?(string)
99
- $KCODE == 'UTF8' && consumes?(string)
100
- end
101
-
102
- # Returns a new Chars object containing the _other_ object concatenated to the string.
103
- #
104
- # Example:
105
- # ('Café'.mb_chars + ' périferôl').to_s # => "Café périferôl"
106
- def +(other)
107
- chars(@wrapped_string + other)
108
- end
109
-
110
- # Like <tt>String#=~</tt> only it returns the character offset (in codepoints) instead of the byte offset.
111
- #
112
- # Example:
113
- # 'Café périferôl'.mb_chars =~ /ô/ # => 12
114
- def =~(other)
115
- translate_offset(@wrapped_string =~ other)
116
- end
117
-
118
- # Inserts the passed string at specified codepoint offsets.
119
- #
120
- # Example:
121
- # 'Café'.mb_chars.insert(4, ' périferôl').to_s # => "Café périferôl"
122
- def insert(offset, fragment)
123
- unpacked = Unicode.u_unpack(@wrapped_string)
124
- unless offset > unpacked.length
125
- @wrapped_string.replace(
126
- Unicode.u_unpack(@wrapped_string).insert(offset, *Unicode.u_unpack(fragment)).pack('U*')
127
- )
128
- else
129
- raise IndexError, "index #{offset} out of string"
130
- end
131
- self
132
- end
133
-
134
- # Returns +true+ if contained string contains _other_. Returns +false+ otherwise.
135
- #
136
- # Example:
137
- # 'Café'.mb_chars.include?('é') # => true
138
- def include?(other)
139
- # We have to redefine this method because Enumerable defines it.
140
- @wrapped_string.include?(other)
141
- end
142
-
143
- # Returns the position _needle_ in the string, counting in codepoints. Returns +nil+ if _needle_ isn't found.
144
- #
145
- # Example:
146
- # 'Café périferôl'.mb_chars.index('ô') # => 12
147
- # 'Café périferôl'.mb_chars.index(/\w/u) # => 0
148
- def index(needle, offset=0)
149
- wrapped_offset = first(offset).wrapped_string.length
150
- index = @wrapped_string.index(needle, wrapped_offset)
151
- index ? (Unicode.u_unpack(@wrapped_string.slice(0...index)).size) : nil
152
- end
153
-
154
- # Returns the position _needle_ in the string, counting in
155
- # codepoints, searching backward from _offset_ or the end of the
156
- # string. Returns +nil+ if _needle_ isn't found.
157
- #
158
- # Example:
159
- # 'Café périferôl'.mb_chars.rindex('é') # => 6
160
- # 'Café périferôl'.mb_chars.rindex(/\w/u) # => 13
161
- def rindex(needle, offset=nil)
162
- offset ||= length
163
- wrapped_offset = first(offset).wrapped_string.length
164
- index = @wrapped_string.rindex(needle, wrapped_offset)
165
- index ? (Unicode.u_unpack(@wrapped_string.slice(0...index)).size) : nil
166
- end
167
-
168
- # Returns the number of codepoints in the string
169
- def size
170
- Unicode.u_unpack(@wrapped_string).size
171
- end
172
- alias_method :length, :size
173
-
174
- # Strips entire range of Unicode whitespace from the right of the string.
175
- def rstrip
176
- chars(@wrapped_string.gsub(Unicode::TRAILERS_PAT, ''))
177
- end
178
-
179
- # Strips entire range of Unicode whitespace from the left of the string.
180
- def lstrip
181
- chars(@wrapped_string.gsub(Unicode::LEADERS_PAT, ''))
182
- end
183
-
184
- # Strips entire range of Unicode whitespace from the right and left of the string.
185
- def strip
186
- rstrip.lstrip
187
- end
188
-
189
- # Returns the codepoint of the first character in the string.
190
- #
191
- # Example:
192
- # 'こんにちは'.mb_chars.ord # => 12371
193
- def ord
194
- Unicode.u_unpack(@wrapped_string)[0]
195
- end
196
-
197
- # Works just like <tt>String#rjust</tt>, only integer specifies characters instead of bytes.
198
- #
199
- # Example:
200
- #
201
- # "¾ cup".mb_chars.rjust(8).to_s
202
- # # => " ¾ cup"
203
- #
204
- # "¾ cup".mb_chars.rjust(8, " ").to_s # Use non-breaking whitespace
205
- # # => "   ¾ cup"
206
- def rjust(integer, padstr=' ')
207
- justify(integer, :right, padstr)
208
- end
209
-
210
- # Works just like <tt>String#ljust</tt>, only integer specifies characters instead of bytes.
211
- #
212
- # Example:
213
- #
214
- # "¾ cup".mb_chars.rjust(8).to_s
215
- # # => "¾ cup "
216
- #
217
- # "¾ cup".mb_chars.rjust(8, " ").to_s # Use non-breaking whitespace
218
- # # => "¾ cup   "
219
- def ljust(integer, padstr=' ')
220
- justify(integer, :left, padstr)
221
- end
222
-
223
- # Works just like <tt>String#center</tt>, only integer specifies characters instead of bytes.
224
- #
225
- # Example:
226
- #
227
- # "¾ cup".mb_chars.center(8).to_s
228
- # # => " ¾ cup "
229
- #
230
- # "¾ cup".mb_chars.center(8, " ").to_s # Use non-breaking whitespace
231
- # # => " ¾ cup  "
232
- def center(integer, padstr=' ')
233
- justify(integer, :center, padstr)
234
- end
235
-
236
- else
237
- def =~(other)
238
- @wrapped_string =~ other
239
- end
82
+ def =~(other)
83
+ @wrapped_string =~ other
240
84
  end
241
85
 
242
86
  # Works just like <tt>String#split</tt>, with the exception that the items in the resulting list are Chars
243
87
  # instances instead of String. This makes chaining methods easier.
244
88
  #
245
89
  # Example:
246
- # 'Café périferôl'.mb_chars.split(/é/).map { |part| part.upcase.to_s } # => ["CAF", " P", "RIFERÔL"]
90
+ # Mail::Multibyte.mb_chars('Café périferôl').split(/é/).map { |part| part.upcase.to_s } # => ["CAF", " P", "RIFERÔL"]
247
91
  def split(*args)
248
92
  @wrapped_string.split(*args).map { |i| i.mb_chars }
249
93
  end
@@ -268,12 +112,12 @@ module Mail #:nodoc:
268
112
  @wrapped_string[*args] = replace_by
269
113
  else
270
114
  result = Unicode.u_unpack(@wrapped_string)
271
- if args[0].is_a?(Fixnum)
115
+ if args[0].is_a?(Integer)
272
116
  raise IndexError, "index #{args[0]} out of string" if args[0] >= result.length
273
117
  min = args[0]
274
118
  max = args[1].nil? ? min : (min + args[1] - 1)
275
119
  range = Range.new(min, max)
276
- replace_by = [replace_by].pack('U') if replace_by.is_a?(Fixnum)
120
+ replace_by = [replace_by].pack('U') if replace_by.is_a?(Integer)
277
121
  elsif args.first.is_a?(Range)
278
122
  raise RangeError, "#{args[0]} out of range" if args[0].min >= result.length
279
123
  range = args[0]
@@ -291,7 +135,7 @@ module Mail #:nodoc:
291
135
  # Reverses all characters in the string.
292
136
  #
293
137
  # Example:
294
- # 'Café'.mb_chars.reverse.to_s # => 'éfaC'
138
+ # Mail::Multibyte.mb_chars('Café').reverse.to_s # => 'éfaC'
295
139
  def reverse
296
140
  chars(Unicode.g_unpack(@wrapped_string).reverse.flatten.pack('U*'))
297
141
  end
@@ -300,7 +144,7 @@ module Mail #:nodoc:
300
144
  # character.
301
145
  #
302
146
  # Example:
303
- # 'こんにちは'.mb_chars.slice(2..3).to_s # => "にち"
147
+ # Mail::Multibyte.mb_chars('こんにちは').slice(2..3).to_s # => "にち"
304
148
  def slice(*args)
305
149
  if args.size > 2
306
150
  raise ArgumentError, "wrong number of arguments (#{args.size} for 1)" # Do as if we were native
@@ -329,7 +173,7 @@ module Mail #:nodoc:
329
173
  #
330
174
  # Example:
331
175
  # s = 'こんにちは'
332
- # s.mb_chars.limit(7) # => "こに"
176
+ # s.mb_chars.limit(7) # => "こん"
333
177
  def limit(limit)
334
178
  slice(0...translate_offset(limit))
335
179
  end
@@ -337,7 +181,7 @@ module Mail #:nodoc:
337
181
  # Convert characters in the string to uppercase.
338
182
  #
339
183
  # Example:
340
- # 'Laurent, où sont les tests ?'.mb_chars.upcase.to_s # => "LAURENT, OÙ SONT LES TESTS ?"
184
+ # Mail::Multibyte.mb_chars('Laurent, où sont les tests ?').upcase.to_s # => "LAURENT, OÙ SONT LES TESTS ?"
341
185
  def upcase
342
186
  chars(Unicode.apply_mapping(@wrapped_string, :uppercase_mapping))
343
187
  end
@@ -345,7 +189,7 @@ module Mail #:nodoc:
345
189
  # Convert characters in the string to lowercase.
346
190
  #
347
191
  # Example:
348
- # 'VĚDA A VÝZKUM'.mb_chars.downcase.to_s # => "věda a výzkum"
192
+ # Mail::Multibyte.mb_chars('VĚDA A VÝZKUM').downcase.to_s # => "věda a výzkum"
349
193
  def downcase
350
194
  chars(Unicode.apply_mapping(@wrapped_string, :lowercase_mapping))
351
195
  end
@@ -353,7 +197,7 @@ module Mail #:nodoc:
353
197
  # Converts the first character to uppercase and the remainder to lowercase.
354
198
  #
355
199
  # Example:
356
- # 'über'.mb_chars.capitalize.to_s # => "Über"
200
+ # Mail::Multibyte.mb_chars('über').capitalize.to_s # => "Über"
357
201
  def capitalize
358
202
  (slice(0) || chars('')).upcase + (slice(1..-1) || chars('')).downcase
359
203
  end
@@ -361,8 +205,8 @@ module Mail #:nodoc:
361
205
  # Capitalizes the first letter of every word, when possible.
362
206
  #
363
207
  # Example:
364
- # "ÉL QUE SE ENTERÓ".mb_chars.titleize # => "Él Que Se Enteró"
365
- # "日本語".mb_chars.titleize # => "日本語"
208
+ # Mail::Multibyte.mb_chars("ÉL QUE SE ENTERÓ").titleize # => "Él Que Se Enteró"
209
+ # Mail::Multibyte.mb_chars("日本語").titleize # => "日本語"
366
210
  def titleize
367
211
  chars(downcase.to_s.gsub(/\b('?\S)/u) { Unicode.apply_mapping $1, :uppercase_mapping })
368
212
  end
@@ -382,7 +226,7 @@ module Mail #:nodoc:
382
226
  #
383
227
  # Example:
384
228
  # 'é'.length # => 2
385
- # 'é'.mb_chars.decompose.to_s.length # => 3
229
+ # Mail::Multibyte.mb_chars('é').decompose.to_s.length # => 3
386
230
  def decompose
387
231
  chars(Unicode.decompose_codepoints(:canonical, Unicode.u_unpack(@wrapped_string)).pack('U*'))
388
232
  end
@@ -391,7 +235,7 @@ module Mail #:nodoc:
391
235
  #
392
236
  # Example:
393
237
  # 'é'.length # => 3
394
- # 'é'.mb_chars.compose.to_s.length # => 2
238
+ # Mail::Multibyte.mb_chars('é').compose.to_s.length # => 2
395
239
  def compose
396
240
  chars(Unicode.compose_codepoints(Unicode.u_unpack(@wrapped_string)).pack('U*'))
397
241
  end
@@ -399,8 +243,8 @@ module Mail #:nodoc:
399
243
  # Returns the number of grapheme clusters in the string.
400
244
  #
401
245
  # Example:
402
- # 'क्षि'.mb_chars.length # => 4
403
- # 'क्षि'.mb_chars.g_length # => 3
246
+ # Mail::Multibyte.mb_chars('क्षि').length # => 4
247
+ # Mail::Multibyte.mb_chars('क्षि').g_length # => 3
404
248
  def g_length
405
249
  Unicode.g_unpack(@wrapped_string).length
406
250
  end
@@ -417,7 +261,7 @@ module Mail #:nodoc:
417
261
  # exclude lstrip!, rstrip! and strip! because they are already work as expected on multibyte strings.
418
262
  if public_method_defined?(method)
419
263
  define_method("#{method}!") do |*args|
420
- @wrapped_string = send(args.nil? ? method : method, *args).to_s
264
+ @wrapped_string = send(method, *args).to_s
421
265
  self
422
266
  end
423
267
  end
@@ -1,6 +1,27 @@
1
+ # frozen_string_literal: true
1
2
  module Mail
2
3
  module Multibyte
3
4
  module Unicode
5
+ # Adapted from https://github.com/rails/rails/blob/master/activesupport/lib/active_support/multibyte/unicode.rb
6
+ # under the MIT license
7
+ # The Unicode version that is supported by the implementation
8
+ UNICODE_VERSION = '7.0.0'
9
+
10
+ # Holds data about a codepoint in the Unicode database.
11
+ class Codepoint
12
+ attr_accessor :code, :combining_class, :decomp_type, :decomp_mapping, :uppercase_mapping, :lowercase_mapping
13
+
14
+ # Initializing Codepoint object with default values
15
+ def initialize
16
+ @combining_class = 0
17
+ @uppercase_mapping = 0
18
+ @lowercase_mapping = 0
19
+ end
20
+
21
+ def swapcase_mapping
22
+ uppercase_mapping > 0 ? uppercase_mapping : lowercase_mapping
23
+ end
24
+ end
4
25
 
5
26
  extend self
6
27
 
@@ -8,9 +29,6 @@ module Mail
8
29
  # information about normalization.
9
30
  NORMALIZATION_FORMS = [:c, :kc, :d, :kd]
10
31
 
11
- # The Unicode version that is supported by the implementation
12
- UNICODE_VERSION = '5.2.0'
13
-
14
32
  # The default normalization used for operations that require normalization. It can be set to any of the
15
33
  # normalizations in NORMALIZATION_FORMS.
16
34
  #
@@ -284,16 +302,16 @@ module Mail
284
302
  # See http://www.unicode.org/reports/tr15, Table 1
285
303
  codepoints = u_unpack(string)
286
304
  case form
287
- when :d
288
- reorder_characters(decompose_codepoints(:canonical, codepoints))
289
- when :c
290
- compose_codepoints(reorder_characters(decompose_codepoints(:canonical, codepoints)))
291
- when :kd
292
- reorder_characters(decompose_codepoints(:compatability, codepoints))
293
- when :kc
294
- compose_codepoints(reorder_characters(decompose_codepoints(:compatability, codepoints)))
295
- else
296
- raise ArgumentError, "#{form} is not a valid normalization variant", caller
305
+ when :d
306
+ reorder_characters(decompose_codepoints(:canonical, codepoints))
307
+ when :c
308
+ compose_codepoints(reorder_characters(decompose_codepoints(:canonical, codepoints)))
309
+ when :kd
310
+ reorder_characters(decompose_codepoints(:compatability, codepoints))
311
+ when :kc
312
+ compose_codepoints(reorder_characters(decompose_codepoints(:compatability, codepoints)))
313
+ else
314
+ raise ArgumentError, "#{form} is not a valid normalization variant", caller
297
315
  end.pack('U*')
298
316
  end
299
317
 
@@ -308,11 +326,6 @@ module Mail
308
326
  end.pack('U*')
309
327
  end
310
328
 
311
- # Holds data about a codepoint in the Unicode database
312
- class Codepoint
313
- attr_accessor :code, :combining_class, :decomp_type, :decomp_mapping, :uppercase_mapping, :lowercase_mapping
314
- end
315
-
316
329
  # Holds static data from the Unicode database
317
330
  class UnicodeDatabase
318
331
  ATTRIBUTES = :codepoints, :composition_exclusion, :composition_map, :boundary, :cp1252
@@ -341,7 +354,7 @@ module Mail
341
354
  def load
342
355
  begin
343
356
  @codepoints, @composition_exclusion, @composition_map, @boundary, @cp1252 = File.open(self.class.filename, 'rb') { |f| Marshal.load f.read }
344
- rescue Exception => e
357
+ rescue => e
345
358
  raise IOError.new("Couldn't load the Unicode tables for UTF8Handler (#{e.message}), Mail::Multibyte is unusable")
346
359
  end
347
360
 
@@ -390,11 +403,3 @@ module Mail
390
403
  end
391
404
  end
392
405
  end
393
-
394
- unless defined?(ActiveSupport)
395
- module ActiveSupport
396
- unless const_defined?(:Multibyte)
397
- Multibyte = Mail::Multibyte
398
- end
399
- end
400
- end
@@ -1,60 +1,44 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module Mail #:nodoc:
4
5
  module Multibyte #:nodoc:
5
- if RUBY_VERSION >= "1.9"
6
- # Returns a regular expression that matches valid characters in the current encoding
7
- def self.valid_character
8
- VALID_CHARACTER[Encoding.default_external.to_s]
9
- end
10
- else
11
- def self.valid_character
12
- case $KCODE
13
- when 'UTF8'
14
- VALID_CHARACTER['UTF-8']
15
- when 'SJIS'
16
- VALID_CHARACTER['Shift_JIS']
17
- end
18
- end
6
+ # Returns a regular expression that matches valid characters in the current encoding
7
+ def self.valid_character
8
+ VALID_CHARACTER[Encoding.default_external.to_s]
19
9
  end
20
10
 
21
- if 'string'.respond_to?(:valid_encoding?)
22
- # Verifies the encoding of a string
23
- def self.verify(string)
24
- string.valid_encoding?
25
- end
26
- else
27
- def self.verify(string)
28
- if expression = valid_character
29
- # Splits the string on character boundaries, which are determined based on $KCODE.
30
- string.split(//).all? { |c| expression =~ c }
31
- else
32
- true
33
- end
11
+ # Returns true if string has valid utf-8 encoding
12
+ def self.is_utf8?(string)
13
+ case string.encoding
14
+ when Encoding::UTF_8
15
+ verify(string)
16
+ when Encoding::ASCII_8BIT, Encoding::US_ASCII
17
+ verify(to_utf8(string))
18
+ else
19
+ false
34
20
  end
35
21
  end
36
22
 
23
+ # Verifies the encoding of a string
24
+ def self.verify(string)
25
+ string.valid_encoding?
26
+ end
27
+
37
28
  # Verifies the encoding of the string and raises an exception when it's not valid
38
29
  def self.verify!(string)
39
30
  raise EncodingError.new("Found characters with invalid encoding") unless verify(string)
40
31
  end
41
32
 
42
- if 'string'.respond_to?(:force_encoding)
43
- # Removes all invalid characters from the string.
44
- #
45
- # Note: this method is a no-op in Ruby 1.9
46
- def self.clean(string)
47
- string
48
- end
49
- else
50
- def self.clean(string)
51
- if expression = valid_character
52
- # Splits the string on character boundaries, which are determined based on $KCODE.
53
- string.split(//).grep(expression).join
54
- else
55
- string
56
- end
57
- end
33
+ # Removes all invalid characters from the string.
34
+ #
35
+ # Note: this method is a no-op in Ruby 1.9
36
+ def self.clean(string)
37
+ string
38
+ end
39
+
40
+ def self.to_utf8(string)
41
+ string.dup.force_encoding(Encoding::UTF_8)
58
42
  end
59
43
  end
60
44
  end
@@ -1,24 +1,64 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
3
+ require 'mail/multibyte/chars'
4
+
2
5
  module Mail #:nodoc:
3
6
  module Multibyte
4
- require 'mail/multibyte/exceptions'
5
- require 'mail/multibyte/chars'
6
- require 'mail/multibyte/unicode'
7
+ # Raised when a problem with the encoding was found.
8
+ class EncodingError < StandardError; end
7
9
 
8
- # The proxy class returned when calling mb_chars. You can use this accessor to configure your own proxy
9
- # class so you can support other encodings. See the Mail::Multibyte::Chars implementation for
10
- # an example how to do this.
11
- #
12
- # Example:
13
- # Mail::Multibyte.proxy_class = CharsForUTF32
14
- def self.proxy_class=(klass)
15
- @proxy_class = klass
10
+ class << self
11
+ # The proxy class returned when calling mb_chars. You can use this accessor to configure your own proxy
12
+ # class so you can support other encodings. See the Mail::Multibyte::Chars implementation for
13
+ # an example how to do this.
14
+ #
15
+ # Example:
16
+ # Mail::Multibyte.proxy_class = CharsForUTF32
17
+ attr_accessor :proxy_class
16
18
  end
17
19
 
18
- # Returns the current proxy class
19
- def self.proxy_class
20
- @proxy_class ||= Mail::Multibyte::Chars
21
- end
20
+ self.proxy_class = Mail::Multibyte::Chars
21
+
22
+ # == Multibyte proxy
23
+ #
24
+ # +mb_chars+ is a multibyte safe proxy for string methods.
25
+ #
26
+ # In Ruby 1.8 and older it creates and returns an instance of the Mail::Multibyte::Chars class which
27
+ # encapsulates the original string. A Unicode safe version of all the String methods are defined on this proxy
28
+ # class. If the proxy class doesn't respond to a certain method, it's forwarded to the encapsuled string.
29
+ #
30
+ # name = 'Claus Müller'
31
+ # name.reverse # => "rell??M sualC"
32
+ # name.length # => 13
33
+ #
34
+ # name.mb_chars.reverse.to_s # => "rellüM sualC"
35
+ # name.mb_chars.length # => 12
36
+ #
37
+ # In Ruby 1.9 and newer +mb_chars+ returns +self+ because String is (mostly) encoding aware. This means that
38
+ # it becomes easy to run one version of your code on multiple Ruby versions.
39
+ #
40
+ # == Method chaining
41
+ #
42
+ # All the methods on the Chars proxy which normally return a string will return a Chars object. This allows
43
+ # method chaining on the result of any of these methods.
44
+ #
45
+ # name.mb_chars.reverse.length # => 12
46
+ #
47
+ # == Interoperability and configuration
48
+ #
49
+ # The Chars object tries to be as interchangeable with String objects as possible: sorting and comparing between
50
+ # String and Char work like expected. The bang! methods change the internal string representation in the Chars
51
+ # object. Interoperability problems can be resolved easily with a +to_s+ call.
52
+ #
53
+ # For more information about the methods defined on the Chars proxy see Mail::Multibyte::Chars. For
54
+ # information about how to change the default Multibyte behaviour see Mail::Multibyte.
55
+ def self.mb_chars(str)
56
+ if is_utf8?(str)
57
+ proxy_class.new(str)
58
+ else
59
+ str
60
+ end
61
+ end
22
62
 
23
63
  # Regular expressions that describe valid byte sequences for a character
24
64
  VALID_CHARACTER = {
@@ -39,4 +79,4 @@ module Mail #:nodoc:
39
79
  end
40
80
  end
41
81
 
42
- require 'mail/multibyte/utils'
82
+ require 'mail/multibyte/utils'
@@ -1,5 +1,6 @@
1
- module Mail
1
+ # frozen_string_literal: true
2
2
 
3
+ module Mail
3
4
  # A delivery method implementation which sends via exim.
4
5
  #
5
6
  # To use this, first find out where the exim binary is on your computer,
@@ -38,11 +39,12 @@ module Mail
38
39
  class Exim < Sendmail
39
40
  DEFAULTS = {
40
41
  :location => '/usr/sbin/exim',
41
- :arguments => '-i -t'
42
+ :arguments => %w[ -i -t ]
42
43
  }
43
44
 
44
- def self.call(path, arguments, destinations, encoded_message)
45
- super path, arguments, nil, encoded_message
45
+ # Uses -t option to extract recipients from the message.
46
+ def destinations_for(envelope)
47
+ nil
46
48
  end
47
49
  end
48
50
  end
@@ -1,4 +1,5 @@
1
- require 'mail/check_delivery_params'
1
+ # frozen_string_literal: true
2
+ require 'mail/smtp_envelope'
2
3
 
3
4
  module Mail
4
5
  # FileDelivery class delivers emails into multiple files based on the destination
@@ -12,20 +13,16 @@ module Mail
12
13
  # Make sure the path you specify with :location is writable by the Ruby process
13
14
  # running Mail.
14
15
  class FileDelivery
15
- if RUBY_VERSION >= '1.9.1'
16
- require 'fileutils'
17
- else
18
- require 'ftools'
19
- end
16
+ require 'fileutils'
20
17
 
21
18
  attr_accessor :settings
22
19
 
23
20
  def initialize(values)
24
- self.settings = { :location => './mails' }.merge!(values)
21
+ self.settings = { :location => './mails', :extension => '' }.merge!(values)
25
22
  end
26
23
 
27
24
  def deliver!(mail)
28
- Mail::CheckDeliveryParams.check(mail)
25
+ envelope = Mail::SmtpEnvelope.new(mail)
29
26
 
30
27
  if ::File.respond_to?(:makedirs)
31
28
  ::File.makedirs settings[:location]
@@ -33,8 +30,13 @@ module Mail
33
30
  ::FileUtils.mkdir_p settings[:location]
34
31
  end
35
32
 
36
- mail.destinations.uniq.each do |to|
37
- ::File.open(::File.join(settings[:location], File.basename(to.to_s)), 'a') { |f| "#{f.write(mail.encoded)}\r\n\r\n" }
33
+ envelope.to.uniq.each do |to|
34
+ path = ::File.join(settings[:location], File.basename(to.to_s+settings[:extension]))
35
+
36
+ ::File.open(path, 'a') do |f|
37
+ f.write envelope.message
38
+ f.write "\r\n\r\n"
39
+ end
38
40
  end
39
41
  end
40
42
  end