mail 2.6.4 → 2.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (179) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +111 -118
  3. data/lib/mail/attachments_list.rb +11 -10
  4. data/lib/mail/body.rb +73 -84
  5. data/lib/mail/check_delivery_params.rb +54 -10
  6. data/lib/mail/configuration.rb +2 -0
  7. data/lib/mail/constants.rb +27 -5
  8. data/lib/mail/elements/address.rb +61 -50
  9. data/lib/mail/elements/address_list.rb +11 -19
  10. data/lib/mail/elements/content_disposition_element.rb +9 -16
  11. data/lib/mail/elements/content_location_element.rb +6 -11
  12. data/lib/mail/elements/content_transfer_encoding_element.rb +6 -11
  13. data/lib/mail/elements/content_type_element.rb +16 -23
  14. data/lib/mail/elements/date_time_element.rb +7 -15
  15. data/lib/mail/elements/envelope_from_element.rb +22 -23
  16. data/lib/mail/elements/message_ids_element.rb +18 -13
  17. data/lib/mail/elements/mime_version_element.rb +7 -15
  18. data/lib/mail/elements/phrase_list.rb +12 -10
  19. data/lib/mail/elements/received_element.rb +27 -19
  20. data/lib/mail/encodings/7bit.rb +9 -14
  21. data/lib/mail/encodings/8bit.rb +2 -21
  22. data/lib/mail/encodings/base64.rb +11 -12
  23. data/lib/mail/encodings/binary.rb +3 -22
  24. data/lib/mail/encodings/identity.rb +24 -0
  25. data/lib/mail/encodings/quoted_printable.rb +6 -6
  26. data/lib/mail/encodings/transfer_encoding.rb +38 -29
  27. data/lib/mail/encodings/unix_to_unix.rb +3 -1
  28. data/lib/mail/encodings.rb +81 -54
  29. data/lib/mail/envelope.rb +11 -14
  30. data/lib/mail/field.rb +119 -98
  31. data/lib/mail/field_list.rb +60 -7
  32. data/lib/mail/fields/bcc_field.rb +34 -52
  33. data/lib/mail/fields/cc_field.rb +28 -49
  34. data/lib/mail/fields/comments_field.rb +27 -37
  35. data/lib/mail/fields/common_address_field.rb +170 -0
  36. data/lib/mail/fields/common_date_field.rb +58 -0
  37. data/lib/mail/fields/common_field.rb +77 -0
  38. data/lib/mail/fields/common_message_id_field.rb +42 -0
  39. data/lib/mail/fields/content_description_field.rb +7 -14
  40. data/lib/mail/fields/content_disposition_field.rb +13 -38
  41. data/lib/mail/fields/content_id_field.rb +24 -51
  42. data/lib/mail/fields/content_location_field.rb +11 -25
  43. data/lib/mail/fields/content_transfer_encoding_field.rb +31 -31
  44. data/lib/mail/fields/content_type_field.rb +50 -80
  45. data/lib/mail/fields/date_field.rb +23 -52
  46. data/lib/mail/fields/from_field.rb +28 -49
  47. data/lib/mail/fields/in_reply_to_field.rb +38 -49
  48. data/lib/mail/fields/keywords_field.rb +18 -31
  49. data/lib/mail/fields/message_id_field.rb +25 -71
  50. data/lib/mail/fields/mime_version_field.rb +19 -30
  51. data/lib/mail/fields/named_structured_field.rb +11 -0
  52. data/lib/mail/fields/named_unstructured_field.rb +11 -0
  53. data/lib/mail/fields/optional_field.rb +9 -7
  54. data/lib/mail/fields/{common/parameter_hash.rb → parameter_hash.rb} +13 -11
  55. data/lib/mail/fields/received_field.rb +43 -57
  56. data/lib/mail/fields/references_field.rb +35 -49
  57. data/lib/mail/fields/reply_to_field.rb +28 -49
  58. data/lib/mail/fields/resent_bcc_field.rb +28 -49
  59. data/lib/mail/fields/resent_cc_field.rb +28 -49
  60. data/lib/mail/fields/resent_date_field.rb +5 -30
  61. data/lib/mail/fields/resent_from_field.rb +28 -49
  62. data/lib/mail/fields/resent_message_id_field.rb +5 -29
  63. data/lib/mail/fields/resent_sender_field.rb +27 -56
  64. data/lib/mail/fields/resent_to_field.rb +28 -49
  65. data/lib/mail/fields/return_path_field.rb +50 -54
  66. data/lib/mail/fields/sender_field.rb +34 -55
  67. data/lib/mail/fields/structured_field.rb +3 -30
  68. data/lib/mail/fields/subject_field.rb +9 -11
  69. data/lib/mail/fields/to_field.rb +28 -49
  70. data/lib/mail/fields/unstructured_field.rb +32 -47
  71. data/lib/mail/header.rb +71 -110
  72. data/lib/mail/mail.rb +2 -10
  73. data/lib/mail/matchers/attachment_matchers.rb +15 -0
  74. data/lib/mail/matchers/has_sent_mail.rb +21 -1
  75. data/lib/mail/message.rb +113 -117
  76. data/lib/mail/multibyte/chars.rb +23 -180
  77. data/lib/mail/multibyte/unicode.rb +10 -10
  78. data/lib/mail/multibyte/utils.rb +26 -43
  79. data/lib/mail/multibyte.rb +55 -16
  80. data/lib/mail/network/delivery_methods/exim.rb +8 -11
  81. data/lib/mail/network/delivery_methods/file_delivery.rb +13 -16
  82. data/lib/mail/network/delivery_methods/logger_delivery.rb +34 -0
  83. data/lib/mail/network/delivery_methods/sendmail.rb +32 -35
  84. data/lib/mail/network/delivery_methods/smtp.rb +76 -54
  85. data/lib/mail/network/delivery_methods/smtp_connection.rb +4 -9
  86. data/lib/mail/network/delivery_methods/test_mailer.rb +8 -9
  87. data/lib/mail/network/retriever_methods/base.rb +8 -8
  88. data/lib/mail/network/retriever_methods/imap.rb +20 -7
  89. data/lib/mail/network/retriever_methods/pop3.rb +5 -3
  90. data/lib/mail/network/retriever_methods/test_retriever.rb +3 -2
  91. data/lib/mail/network.rb +1 -0
  92. data/lib/mail/parser_tools.rb +15 -0
  93. data/lib/mail/parsers/address_lists_parser.rb +33225 -116
  94. data/lib/mail/parsers/address_lists_parser.rl +179 -0
  95. data/lib/mail/parsers/content_disposition_parser.rb +882 -49
  96. data/lib/mail/parsers/content_disposition_parser.rl +89 -0
  97. data/lib/mail/parsers/content_location_parser.rb +809 -23
  98. data/lib/mail/parsers/content_location_parser.rl +78 -0
  99. data/lib/mail/parsers/content_transfer_encoding_parser.rb +509 -21
  100. data/lib/mail/parsers/content_transfer_encoding_parser.rl +71 -0
  101. data/lib/mail/parsers/content_type_parser.rb +1037 -56
  102. data/lib/mail/parsers/content_type_parser.rl +90 -0
  103. data/lib/mail/parsers/date_time_parser.rb +877 -25
  104. data/lib/mail/parsers/date_time_parser.rl +69 -0
  105. data/lib/mail/parsers/envelope_from_parser.rb +3669 -40
  106. data/lib/mail/parsers/envelope_from_parser.rl +89 -0
  107. data/lib/mail/parsers/message_ids_parser.rb +5146 -25
  108. data/lib/mail/parsers/message_ids_parser.rl +93 -0
  109. data/lib/mail/parsers/mime_version_parser.rb +497 -26
  110. data/lib/mail/parsers/mime_version_parser.rl +68 -0
  111. data/lib/mail/parsers/phrase_lists_parser.rb +870 -22
  112. data/lib/mail/parsers/phrase_lists_parser.rl +90 -0
  113. data/lib/mail/parsers/received_parser.rb +8776 -43
  114. data/lib/mail/parsers/received_parser.rl +91 -0
  115. data/lib/mail/parsers/rfc2045_content_transfer_encoding.rl +13 -0
  116. data/lib/mail/parsers/rfc2045_content_type.rl +25 -0
  117. data/lib/mail/parsers/rfc2045_mime.rl +16 -0
  118. data/lib/mail/parsers/rfc2183_content_disposition.rl +15 -0
  119. data/lib/mail/parsers/rfc3629_utf8.rl +19 -0
  120. data/lib/mail/parsers/rfc5234_abnf_core_rules.rl +22 -0
  121. data/lib/mail/parsers/rfc5322.rl +74 -0
  122. data/lib/mail/parsers/rfc5322_address.rl +72 -0
  123. data/lib/mail/parsers/{ragel/date_time.rl → rfc5322_date_time.rl} +8 -1
  124. data/lib/mail/parsers/rfc5322_lexical_tokens.rl +60 -0
  125. data/lib/mail/parsers.rb +11 -25
  126. data/lib/mail/part.rb +6 -10
  127. data/lib/mail/parts_list.rb +62 -6
  128. data/lib/mail/smtp_envelope.rb +57 -0
  129. data/lib/mail/utilities.rb +357 -74
  130. data/lib/mail/version.rb +2 -2
  131. data/lib/mail/yaml.rb +30 -0
  132. data/lib/mail.rb +5 -35
  133. metadata +111 -66
  134. data/CHANGELOG.rdoc +0 -787
  135. data/CONTRIBUTING.md +0 -60
  136. data/Dependencies.txt +0 -2
  137. data/Gemfile +0 -11
  138. data/Rakefile +0 -29
  139. data/TODO.rdoc +0 -9
  140. data/lib/mail/core_extensions/smtp.rb +0 -25
  141. data/lib/mail/core_extensions/string/access.rb +0 -146
  142. data/lib/mail/core_extensions/string/multibyte.rb +0 -79
  143. data/lib/mail/core_extensions/string.rb +0 -21
  144. data/lib/mail/fields/common/address_container.rb +0 -17
  145. data/lib/mail/fields/common/common_address.rb +0 -136
  146. data/lib/mail/fields/common/common_date.rb +0 -36
  147. data/lib/mail/fields/common/common_field.rb +0 -61
  148. data/lib/mail/fields/common/common_message_id.rb +0 -49
  149. data/lib/mail/multibyte/exceptions.rb +0 -9
  150. data/lib/mail/parsers/ragel/common.rl +0 -185
  151. data/lib/mail/parsers/ragel/parser_info.rb +0 -61
  152. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +0 -14864
  153. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +0 -37
  154. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +0 -751
  155. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +0 -37
  156. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +0 -614
  157. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +0 -37
  158. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +0 -447
  159. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +0 -37
  160. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +0 -825
  161. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +0 -37
  162. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +0 -817
  163. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +0 -37
  164. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +0 -2149
  165. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +0 -37
  166. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +0 -1570
  167. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +0 -37
  168. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +0 -440
  169. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +0 -37
  170. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +0 -564
  171. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +0 -37
  172. data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +0 -51
  173. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +0 -5144
  174. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +0 -37
  175. data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +0 -37
  176. data/lib/mail/parsers/ragel/ruby.rb +0 -40
  177. data/lib/mail/parsers/ragel.rb +0 -18
  178. data/lib/mail/version_specific/ruby_1_8.rb +0 -126
  179. data/lib/mail/version_specific/ruby_1_9.rb +0 -223
@@ -1,5 +1,6 @@
1
1
  # encoding: utf-8
2
2
  # frozen_string_literal: true
3
+ require 'mail/multibyte/unicode'
3
4
 
4
5
  module Mail #:nodoc:
5
6
  module Multibyte #:nodoc:
@@ -37,16 +38,10 @@ module Mail #:nodoc:
37
38
  alias to_s wrapped_string
38
39
  alias to_str wrapped_string
39
40
 
40
- if RUBY_VERSION >= "1.9"
41
- # Creates a new Chars instance by wrapping _string_.
42
- def initialize(string)
43
- @wrapped_string = string
44
- @wrapped_string.force_encoding(Encoding::UTF_8) unless @wrapped_string.frozen?
45
- end
46
- else
47
- def initialize(string) #:nodoc:
48
- @wrapped_string = string
49
- 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?
50
45
  end
51
46
 
52
47
  # Forward all undefined methods to the wrapped string.
@@ -71,15 +66,6 @@ module Mail #:nodoc:
71
66
  true
72
67
  end
73
68
 
74
- # Returns +true+ when the proxy class can handle the string. Returns +false+ otherwise.
75
- def self.consumes?(string)
76
- # Unpack is a little bit faster than regular expressions.
77
- string.unpack('U*')
78
- true
79
- rescue ArgumentError
80
- false
81
- end
82
-
83
69
  include Comparable
84
70
 
85
71
  # Returns -1, 0, or 1, depending on whether the Chars object is to be sorted before,
@@ -93,158 +79,15 @@ module Mail #:nodoc:
93
79
  @wrapped_string <=> other.to_s
94
80
  end
95
81
 
96
- if RUBY_VERSION < "1.9"
97
- # Returns +true+ if the Chars class can and should act as a proxy for the string _string_. Returns
98
- # +false+ otherwise.
99
- def self.wants?(string)
100
- $KCODE == 'UTF8' && consumes?(string)
101
- end
102
-
103
- # Returns a new Chars object containing the _other_ object concatenated to the string.
104
- #
105
- # Example:
106
- # ('Café'.mb_chars + ' périferôl').to_s # => "Café périferôl"
107
- def +(other)
108
- chars(@wrapped_string + other)
109
- end
110
-
111
- # Like <tt>String#=~</tt> only it returns the character offset (in codepoints) instead of the byte offset.
112
- #
113
- # Example:
114
- # 'Café périferôl'.mb_chars =~ /ô/ # => 12
115
- def =~(other)
116
- translate_offset(@wrapped_string =~ other)
117
- end
118
-
119
- # Inserts the passed string at specified codepoint offsets.
120
- #
121
- # Example:
122
- # 'Café'.mb_chars.insert(4, ' périferôl').to_s # => "Café périferôl"
123
- def insert(offset, fragment)
124
- unpacked = Unicode.u_unpack(@wrapped_string)
125
- unless offset > unpacked.length
126
- @wrapped_string.replace(
127
- Unicode.u_unpack(@wrapped_string).insert(offset, *Unicode.u_unpack(fragment)).pack('U*')
128
- )
129
- else
130
- raise IndexError, "index #{offset} out of string"
131
- end
132
- self
133
- end
134
-
135
- # Returns +true+ if contained string contains _other_. Returns +false+ otherwise.
136
- #
137
- # Example:
138
- # 'Café'.mb_chars.include?('é') # => true
139
- def include?(other)
140
- # We have to redefine this method because Enumerable defines it.
141
- @wrapped_string.include?(other)
142
- end
143
-
144
- # Returns the position _needle_ in the string, counting in codepoints. Returns +nil+ if _needle_ isn't found.
145
- #
146
- # Example:
147
- # 'Café périferôl'.mb_chars.index('ô') # => 12
148
- # 'Café périferôl'.mb_chars.index(/\w/u) # => 0
149
- def index(needle, offset=0)
150
- wrapped_offset = first(offset).wrapped_string.length
151
- index = @wrapped_string.index(needle, wrapped_offset)
152
- index ? (Unicode.u_unpack(@wrapped_string.slice(0...index)).size) : nil
153
- end
154
-
155
- # Returns the position _needle_ in the string, counting in
156
- # codepoints, searching backward from _offset_ or the end of the
157
- # string. Returns +nil+ if _needle_ isn't found.
158
- #
159
- # Example:
160
- # 'Café périferôl'.mb_chars.rindex('é') # => 6
161
- # 'Café périferôl'.mb_chars.rindex(/\w/u) # => 13
162
- def rindex(needle, offset=nil)
163
- offset ||= length
164
- wrapped_offset = first(offset).wrapped_string.length
165
- index = @wrapped_string.rindex(needle, wrapped_offset)
166
- index ? (Unicode.u_unpack(@wrapped_string.slice(0...index)).size) : nil
167
- end
168
-
169
- # Returns the number of codepoints in the string
170
- def size
171
- Unicode.u_unpack(@wrapped_string).size
172
- end
173
- alias_method :length, :size
174
-
175
- # Strips entire range of Unicode whitespace from the right of the string.
176
- def rstrip
177
- chars(@wrapped_string.gsub(Unicode::TRAILERS_PAT, ''))
178
- end
179
-
180
- # Strips entire range of Unicode whitespace from the left of the string.
181
- def lstrip
182
- chars(@wrapped_string.gsub(Unicode::LEADERS_PAT, ''))
183
- end
184
-
185
- # Strips entire range of Unicode whitespace from the right and left of the string.
186
- def strip
187
- rstrip.lstrip
188
- end
189
-
190
- # Returns the codepoint of the first character in the string.
191
- #
192
- # Example:
193
- # 'こんにちは'.mb_chars.ord # => 12371
194
- def ord
195
- Unicode.u_unpack(@wrapped_string)[0]
196
- end
197
-
198
- # Works just like <tt>String#rjust</tt>, only integer specifies characters instead of bytes.
199
- #
200
- # Example:
201
- #
202
- # "¾ cup".mb_chars.rjust(8).to_s
203
- # # => " ¾ cup"
204
- #
205
- # "¾ cup".mb_chars.rjust(8, " ").to_s # Use non-breaking whitespace
206
- # # => "   ¾ cup"
207
- def rjust(integer, padstr=' ')
208
- justify(integer, :right, padstr)
209
- end
210
-
211
- # Works just like <tt>String#ljust</tt>, only integer specifies characters instead of bytes.
212
- #
213
- # Example:
214
- #
215
- # "¾ cup".mb_chars.rjust(8).to_s
216
- # # => "¾ cup "
217
- #
218
- # "¾ cup".mb_chars.rjust(8, " ").to_s # Use non-breaking whitespace
219
- # # => "¾ cup   "
220
- def ljust(integer, padstr=' ')
221
- justify(integer, :left, padstr)
222
- end
223
-
224
- # Works just like <tt>String#center</tt>, only integer specifies characters instead of bytes.
225
- #
226
- # Example:
227
- #
228
- # "¾ cup".mb_chars.center(8).to_s
229
- # # => " ¾ cup "
230
- #
231
- # "¾ cup".mb_chars.center(8, " ").to_s # Use non-breaking whitespace
232
- # # => " ¾ cup  "
233
- def center(integer, padstr=' ')
234
- justify(integer, :center, padstr)
235
- end
236
-
237
- else
238
- def =~(other)
239
- @wrapped_string =~ other
240
- end
82
+ def =~(other)
83
+ @wrapped_string =~ other
241
84
  end
242
85
 
243
86
  # Works just like <tt>String#split</tt>, with the exception that the items in the resulting list are Chars
244
87
  # instances instead of String. This makes chaining methods easier.
245
88
  #
246
89
  # Example:
247
- # '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"]
248
91
  def split(*args)
249
92
  @wrapped_string.split(*args).map { |i| i.mb_chars }
250
93
  end
@@ -269,12 +112,12 @@ module Mail #:nodoc:
269
112
  @wrapped_string[*args] = replace_by
270
113
  else
271
114
  result = Unicode.u_unpack(@wrapped_string)
272
- if args[0].is_a?(Fixnum)
115
+ if args[0].is_a?(Integer)
273
116
  raise IndexError, "index #{args[0]} out of string" if args[0] >= result.length
274
117
  min = args[0]
275
118
  max = args[1].nil? ? min : (min + args[1] - 1)
276
119
  range = Range.new(min, max)
277
- 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)
278
121
  elsif args.first.is_a?(Range)
279
122
  raise RangeError, "#{args[0]} out of range" if args[0].min >= result.length
280
123
  range = args[0]
@@ -292,7 +135,7 @@ module Mail #:nodoc:
292
135
  # Reverses all characters in the string.
293
136
  #
294
137
  # Example:
295
- # 'Café'.mb_chars.reverse.to_s # => 'éfaC'
138
+ # Mail::Multibyte.mb_chars('Café').reverse.to_s # => 'éfaC'
296
139
  def reverse
297
140
  chars(Unicode.g_unpack(@wrapped_string).reverse.flatten.pack('U*'))
298
141
  end
@@ -301,7 +144,7 @@ module Mail #:nodoc:
301
144
  # character.
302
145
  #
303
146
  # Example:
304
- # 'こんにちは'.mb_chars.slice(2..3).to_s # => "にち"
147
+ # Mail::Multibyte.mb_chars('こんにちは').slice(2..3).to_s # => "にち"
305
148
  def slice(*args)
306
149
  if args.size > 2
307
150
  raise ArgumentError, "wrong number of arguments (#{args.size} for 1)" # Do as if we were native
@@ -330,7 +173,7 @@ module Mail #:nodoc:
330
173
  #
331
174
  # Example:
332
175
  # s = 'こんにちは'
333
- # s.mb_chars.limit(7) # => "こに"
176
+ # s.mb_chars.limit(7) # => "こん"
334
177
  def limit(limit)
335
178
  slice(0...translate_offset(limit))
336
179
  end
@@ -338,7 +181,7 @@ module Mail #:nodoc:
338
181
  # Convert characters in the string to uppercase.
339
182
  #
340
183
  # Example:
341
- # '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 ?"
342
185
  def upcase
343
186
  chars(Unicode.apply_mapping(@wrapped_string, :uppercase_mapping))
344
187
  end
@@ -346,7 +189,7 @@ module Mail #:nodoc:
346
189
  # Convert characters in the string to lowercase.
347
190
  #
348
191
  # Example:
349
- # '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"
350
193
  def downcase
351
194
  chars(Unicode.apply_mapping(@wrapped_string, :lowercase_mapping))
352
195
  end
@@ -354,7 +197,7 @@ module Mail #:nodoc:
354
197
  # Converts the first character to uppercase and the remainder to lowercase.
355
198
  #
356
199
  # Example:
357
- # 'über'.mb_chars.capitalize.to_s # => "Über"
200
+ # Mail::Multibyte.mb_chars('über').capitalize.to_s # => "Über"
358
201
  def capitalize
359
202
  (slice(0) || chars('')).upcase + (slice(1..-1) || chars('')).downcase
360
203
  end
@@ -362,8 +205,8 @@ module Mail #:nodoc:
362
205
  # Capitalizes the first letter of every word, when possible.
363
206
  #
364
207
  # Example:
365
- # "ÉL QUE SE ENTERÓ".mb_chars.titleize # => "Él Que Se Enteró"
366
- # "日本語".mb_chars.titleize # => "日本語"
208
+ # Mail::Multibyte.mb_chars("ÉL QUE SE ENTERÓ").titleize # => "Él Que Se Enteró"
209
+ # Mail::Multibyte.mb_chars("日本語").titleize # => "日本語"
367
210
  def titleize
368
211
  chars(downcase.to_s.gsub(/\b('?\S)/u) { Unicode.apply_mapping $1, :uppercase_mapping })
369
212
  end
@@ -383,7 +226,7 @@ module Mail #:nodoc:
383
226
  #
384
227
  # Example:
385
228
  # 'é'.length # => 2
386
- # 'é'.mb_chars.decompose.to_s.length # => 3
229
+ # Mail::Multibyte.mb_chars('é').decompose.to_s.length # => 3
387
230
  def decompose
388
231
  chars(Unicode.decompose_codepoints(:canonical, Unicode.u_unpack(@wrapped_string)).pack('U*'))
389
232
  end
@@ -392,7 +235,7 @@ module Mail #:nodoc:
392
235
  #
393
236
  # Example:
394
237
  # 'é'.length # => 3
395
- # 'é'.mb_chars.compose.to_s.length # => 2
238
+ # Mail::Multibyte.mb_chars('é').compose.to_s.length # => 2
396
239
  def compose
397
240
  chars(Unicode.compose_codepoints(Unicode.u_unpack(@wrapped_string)).pack('U*'))
398
241
  end
@@ -400,8 +243,8 @@ module Mail #:nodoc:
400
243
  # Returns the number of grapheme clusters in the string.
401
244
  #
402
245
  # Example:
403
- # 'क्षि'.mb_chars.length # => 4
404
- # 'क्षि'.mb_chars.g_length # => 3
246
+ # Mail::Multibyte.mb_chars('क्षि').length # => 4
247
+ # Mail::Multibyte.mb_chars('क्षि').g_length # => 3
405
248
  def g_length
406
249
  Unicode.g_unpack(@wrapped_string).length
407
250
  end
@@ -418,7 +261,7 @@ module Mail #:nodoc:
418
261
  # exclude lstrip!, rstrip! and strip! because they are already work as expected on multibyte strings.
419
262
  if public_method_defined?(method)
420
263
  define_method("#{method}!") do |*args|
421
- @wrapped_string = send(args.nil? ? method : method, *args).to_s
264
+ @wrapped_string = send(method, *args).to_s
422
265
  self
423
266
  end
424
267
  end
@@ -302,16 +302,16 @@ module Mail
302
302
  # See http://www.unicode.org/reports/tr15, Table 1
303
303
  codepoints = u_unpack(string)
304
304
  case form
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
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
315
315
  end.pack('U*')
316
316
  end
317
317
 
@@ -3,59 +3,42 @@
3
3
 
4
4
  module Mail #:nodoc:
5
5
  module Multibyte #:nodoc:
6
- if RUBY_VERSION >= "1.9"
7
- # Returns a regular expression that matches valid characters in the current encoding
8
- def self.valid_character
9
- VALID_CHARACTER[Encoding.default_external.to_s]
10
- end
11
- else
12
- def self.valid_character
13
- case $KCODE
14
- when 'UTF8'
15
- VALID_CHARACTER['UTF-8']
16
- when 'SJIS'
17
- VALID_CHARACTER['Shift_JIS']
18
- end
19
- 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]
20
9
  end
21
10
 
22
- if 'string'.respond_to?(:valid_encoding?)
23
- # Verifies the encoding of a string
24
- def self.verify(string)
25
- string.valid_encoding?
26
- end
27
- else
28
- def self.verify(string)
29
- if expression = valid_character
30
- # Splits the string on character boundaries, which are determined based on $KCODE.
31
- string.split(//).all? { |c| expression =~ c }
32
- else
33
- true
34
- 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
35
20
  end
36
21
  end
37
22
 
23
+ # Verifies the encoding of a string
24
+ def self.verify(string)
25
+ string.valid_encoding?
26
+ end
27
+
38
28
  # Verifies the encoding of the string and raises an exception when it's not valid
39
29
  def self.verify!(string)
40
30
  raise EncodingError.new("Found characters with invalid encoding") unless verify(string)
41
31
  end
42
32
 
43
- if 'string'.respond_to?(:force_encoding)
44
- # Removes all invalid characters from the string.
45
- #
46
- # Note: this method is a no-op in Ruby 1.9
47
- def self.clean(string)
48
- string
49
- end
50
- else
51
- def self.clean(string)
52
- if expression = valid_character
53
- # Splits the string on character boundaries, which are determined based on $KCODE.
54
- string.split(//).grep(expression).join
55
- else
56
- string
57
- end
58
- 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)
59
42
  end
60
43
  end
61
44
  end
@@ -1,25 +1,64 @@
1
1
  # encoding: utf-8
2
2
  # frozen_string_literal: true
3
+ require 'mail/multibyte/chars'
4
+
3
5
  module Mail #:nodoc:
4
6
  module Multibyte
5
- require 'mail/multibyte/exceptions'
6
- require 'mail/multibyte/chars'
7
- require 'mail/multibyte/unicode'
7
+ # Raised when a problem with the encoding was found.
8
+ class EncodingError < StandardError; end
8
9
 
9
- # The proxy class returned when calling mb_chars. You can use this accessor to configure your own proxy
10
- # class so you can support other encodings. See the Mail::Multibyte::Chars implementation for
11
- # an example how to do this.
12
- #
13
- # Example:
14
- # Mail::Multibyte.proxy_class = CharsForUTF32
15
- def self.proxy_class=(klass)
16
- @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
17
18
  end
18
19
 
19
- # Returns the current proxy class
20
- def self.proxy_class
21
- @proxy_class ||= Mail::Multibyte::Chars
22
- 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
23
62
 
24
63
  # Regular expressions that describe valid byte sequences for a character
25
64
  VALID_CHARACTER = {
@@ -40,4 +79,4 @@ module Mail #:nodoc:
40
79
  end
41
80
  end
42
81
 
43
- require 'mail/multibyte/utils'
82
+ require 'mail/multibyte/utils'
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
- module Mail
3
2
 
3
+ module Mail
4
4
  # A delivery method implementation which sends via exim.
5
5
  #
6
6
  # To use this, first find out where the exim binary is on your computer,
@@ -37,17 +37,14 @@ module Mail
37
37
  #
38
38
  # mail.deliver!
39
39
  class Exim < Sendmail
40
- def initialize(values)
41
- self.settings = { :location => '/usr/sbin/exim',
42
- :arguments => '-i -t' }.merge(values)
43
- end
40
+ DEFAULTS = {
41
+ :location => '/usr/sbin/exim',
42
+ :arguments => %w[ -i -t ]
43
+ }
44
44
 
45
- def self.call(path, arguments, destinations, mail)
46
- popen "#{path} #{arguments}" do |io|
47
- io.puts ::Mail::Utilities.to_lf(mail.encoded)
48
- io.flush
49
- end
45
+ # Uses -t option to extract recipients from the message.
46
+ def destinations_for(envelope)
47
+ nil
50
48
  end
51
-
52
49
  end
53
50
  end
@@ -1,8 +1,7 @@
1
1
  # frozen_string_literal: true
2
- require 'mail/check_delivery_params'
2
+ require 'mail/smtp_envelope'
3
3
 
4
4
  module Mail
5
-
6
5
  # FileDelivery class delivers emails into multiple files based on the destination
7
6
  # address. Each file is appended to if it already exists.
8
7
  #
@@ -14,22 +13,16 @@ module Mail
14
13
  # Make sure the path you specify with :location is writable by the Ruby process
15
14
  # running Mail.
16
15
  class FileDelivery
17
- include Mail::CheckDeliveryParams
16
+ require 'fileutils'
18
17
 
19
- if RUBY_VERSION >= '1.9.1'
20
- require 'fileutils'
21
- else
22
- require 'ftools'
23
- end
18
+ attr_accessor :settings
24
19
 
25
20
  def initialize(values)
26
- self.settings = { :location => './mails' }.merge!(values)
21
+ self.settings = { :location => './mails', :extension => '' }.merge!(values)
27
22
  end
28
-
29
- attr_accessor :settings
30
-
23
+
31
24
  def deliver!(mail)
32
- check_delivery_params(mail)
25
+ envelope = Mail::SmtpEnvelope.new(mail)
33
26
 
34
27
  if ::File.respond_to?(:makedirs)
35
28
  ::File.makedirs settings[:location]
@@ -37,10 +30,14 @@ module Mail
37
30
  ::FileUtils.mkdir_p settings[:location]
38
31
  end
39
32
 
40
- mail.destinations.uniq.each do |to|
41
- ::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
42
40
  end
43
41
  end
44
-
45
42
  end
46
43
  end
@@ -0,0 +1,34 @@
1
+ require 'mail/smtp_envelope'
2
+
3
+ module Mail
4
+ class LoggerDelivery
5
+ attr_reader :logger, :severity, :settings
6
+
7
+ def initialize(settings)
8
+ @settings = settings
9
+ @logger = settings.fetch(:logger) { default_logger }
10
+ @severity = derive_severity(settings[:severity])
11
+ end
12
+
13
+ def deliver!(mail)
14
+ logger.log(severity) { Mail::SmtpEnvelope.new(mail).message }
15
+ end
16
+
17
+ private
18
+ def default_logger
19
+ require 'logger'
20
+ ::Logger.new($stdout)
21
+ end
22
+
23
+ def derive_severity(severity)
24
+ case severity
25
+ when nil
26
+ Logger::INFO
27
+ when Integer
28
+ severity
29
+ else
30
+ Logger.const_get(severity.to_s.upcase)
31
+ end
32
+ end
33
+ end
34
+ end