mail 2.6.3 → 2.7.0
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.
- checksums.yaml +4 -4
- data/MIT-LICENSE +1 -1
- data/README.md +91 -79
- data/lib/mail/attachments_list.rb +11 -5
- data/lib/mail/body.rb +46 -39
- data/lib/mail/check_delivery_params.rb +50 -10
- data/lib/mail/configuration.rb +3 -0
- data/lib/mail/constants.rb +4 -2
- data/lib/mail/core_extensions/smtp.rb +20 -16
- data/lib/mail/core_extensions/string.rb +1 -30
- data/lib/mail/elements/address.rb +43 -32
- data/lib/mail/elements/address_list.rb +11 -18
- data/lib/mail/elements/content_disposition_element.rb +9 -15
- data/lib/mail/elements/content_location_element.rb +8 -12
- data/lib/mail/elements/content_transfer_encoding_element.rb +6 -10
- data/lib/mail/elements/content_type_element.rb +9 -19
- data/lib/mail/elements/date_time_element.rb +7 -14
- data/lib/mail/elements/envelope_from_element.rb +15 -21
- data/lib/mail/elements/message_ids_element.rb +12 -14
- data/lib/mail/elements/mime_version_element.rb +7 -14
- data/lib/mail/elements/phrase_list.rb +7 -9
- data/lib/mail/elements/received_element.rb +10 -15
- data/lib/mail/elements.rb +1 -0
- data/lib/mail/encodings/7bit.rb +6 -15
- data/lib/mail/encodings/8bit.rb +5 -18
- data/lib/mail/encodings/base64.rb +15 -10
- data/lib/mail/encodings/binary.rb +4 -22
- data/lib/mail/encodings/identity.rb +24 -0
- data/lib/mail/encodings/quoted_printable.rb +13 -7
- data/lib/mail/encodings/transfer_encoding.rb +47 -28
- data/lib/mail/encodings/unix_to_unix.rb +3 -1
- data/lib/mail/encodings.rb +114 -60
- data/lib/mail/envelope.rb +2 -1
- data/lib/mail/field.rb +112 -62
- data/lib/mail/field_list.rb +1 -0
- data/lib/mail/fields/bcc_field.rb +17 -5
- data/lib/mail/fields/cc_field.rb +2 -2
- data/lib/mail/fields/comments_field.rb +2 -1
- data/lib/mail/fields/common/address_container.rb +3 -2
- data/lib/mail/fields/common/common_address.rb +40 -14
- data/lib/mail/fields/common/common_date.rb +2 -1
- data/lib/mail/fields/common/common_field.rb +5 -11
- data/lib/mail/fields/common/common_message_id.rb +3 -2
- data/lib/mail/fields/common/parameter_hash.rb +2 -1
- data/lib/mail/fields/content_description_field.rb +2 -1
- data/lib/mail/fields/content_disposition_field.rb +14 -13
- data/lib/mail/fields/content_id_field.rb +5 -4
- data/lib/mail/fields/content_location_field.rb +3 -2
- data/lib/mail/fields/content_transfer_encoding_field.rb +3 -2
- data/lib/mail/fields/content_type_field.rb +6 -10
- data/lib/mail/fields/date_field.rb +4 -4
- data/lib/mail/fields/from_field.rb +2 -2
- data/lib/mail/fields/in_reply_to_field.rb +2 -1
- data/lib/mail/fields/keywords_field.rb +3 -3
- data/lib/mail/fields/message_id_field.rb +3 -2
- data/lib/mail/fields/mime_version_field.rb +4 -3
- data/lib/mail/fields/optional_field.rb +5 -1
- data/lib/mail/fields/received_field.rb +5 -4
- data/lib/mail/fields/references_field.rb +2 -1
- data/lib/mail/fields/reply_to_field.rb +2 -2
- data/lib/mail/fields/resent_bcc_field.rb +2 -2
- data/lib/mail/fields/resent_cc_field.rb +2 -2
- data/lib/mail/fields/resent_date_field.rb +2 -2
- data/lib/mail/fields/resent_from_field.rb +2 -2
- data/lib/mail/fields/resent_message_id_field.rb +2 -1
- data/lib/mail/fields/resent_sender_field.rb +2 -2
- data/lib/mail/fields/resent_to_field.rb +2 -2
- data/lib/mail/fields/return_path_field.rb +2 -2
- data/lib/mail/fields/sender_field.rb +2 -2
- data/lib/mail/fields/structured_field.rb +1 -0
- data/lib/mail/fields/subject_field.rb +2 -1
- data/lib/mail/fields/to_field.rb +2 -2
- data/lib/mail/fields/unstructured_field.rb +25 -7
- data/lib/mail/fields.rb +1 -0
- data/lib/mail/header.rb +15 -12
- data/lib/mail/indifferent_hash.rb +1 -0
- data/lib/mail/mail.rb +3 -10
- data/lib/mail/matchers/attachment_matchers.rb +29 -0
- data/lib/mail/matchers/has_sent_mail.rb +51 -7
- data/lib/mail/message.rb +91 -85
- data/lib/mail/multibyte/chars.rb +32 -30
- data/lib/mail/multibyte/unicode.rb +21 -16
- data/lib/mail/multibyte/utils.rb +1 -0
- data/lib/mail/multibyte.rb +65 -15
- data/lib/mail/network/delivery_methods/exim.rb +7 -10
- data/lib/mail/network/delivery_methods/file_delivery.rb +5 -8
- data/lib/mail/network/delivery_methods/logger_delivery.rb +37 -0
- data/lib/mail/network/delivery_methods/sendmail.rb +17 -11
- data/lib/mail/network/delivery_methods/smtp.rb +60 -53
- data/lib/mail/network/delivery_methods/smtp_connection.rb +11 -6
- data/lib/mail/network/delivery_methods/test_mailer.rb +6 -8
- data/lib/mail/network/retriever_methods/base.rb +1 -0
- data/lib/mail/network/retriever_methods/imap.rb +19 -5
- data/lib/mail/network/retriever_methods/pop3.rb +4 -1
- data/lib/mail/network/retriever_methods/test_retriever.rb +2 -1
- data/lib/mail/network.rb +2 -0
- data/lib/mail/parser_tools.rb +15 -0
- data/lib/mail/parsers/address_lists_parser.rb +33187 -112
- data/lib/mail/parsers/address_lists_parser.rl +172 -0
- data/lib/mail/parsers/content_disposition_parser.rb +889 -55
- data/lib/mail/parsers/content_disposition_parser.rl +82 -0
- data/lib/mail/parsers/content_location_parser.rb +801 -23
- data/lib/mail/parsers/content_location_parser.rl +71 -0
- data/lib/mail/parsers/content_transfer_encoding_parser.rb +511 -22
- data/lib/mail/parsers/content_transfer_encoding_parser.rl +64 -0
- data/lib/mail/parsers/content_type_parser.rb +1033 -54
- data/lib/mail/parsers/content_type_parser.rl +83 -0
- data/lib/mail/parsers/date_time_parser.rb +875 -25
- data/lib/mail/parsers/date_time_parser.rl +62 -0
- data/lib/mail/parsers/envelope_from_parser.rb +3565 -36
- data/lib/mail/parsers/envelope_from_parser.rl +82 -0
- data/lib/mail/parsers/message_ids_parser.rb +2844 -25
- data/lib/mail/parsers/message_ids_parser.rl +82 -0
- data/lib/mail/parsers/mime_version_parser.rb +494 -26
- data/lib/mail/parsers/mime_version_parser.rl +61 -0
- data/lib/mail/parsers/phrase_lists_parser.rb +876 -18
- data/lib/mail/parsers/phrase_lists_parser.rl +83 -0
- data/lib/mail/parsers/received_parser.rb +8744 -37
- data/lib/mail/parsers/received_parser.rl +84 -0
- data/lib/mail/parsers/rfc2045_content_transfer_encoding.rl +13 -0
- data/lib/mail/parsers/rfc2045_content_type.rl +25 -0
- data/lib/mail/parsers/rfc2045_mime.rl +16 -0
- data/lib/mail/parsers/rfc2183_content_disposition.rl +15 -0
- data/lib/mail/parsers/rfc3629_utf8.rl +19 -0
- data/lib/mail/parsers/rfc5234_abnf_core_rules.rl +22 -0
- data/lib/mail/parsers/rfc5322.rl +59 -0
- data/lib/mail/parsers/rfc5322_address.rl +72 -0
- data/lib/mail/parsers/{ragel/date_time.rl → rfc5322_date_time.rl} +8 -1
- data/lib/mail/parsers/rfc5322_lexical_tokens.rl +60 -0
- data/lib/mail/parsers.rb +17 -24
- data/lib/mail/part.rb +6 -3
- data/lib/mail/parts_list.rb +31 -14
- data/lib/mail/utilities.rb +107 -8
- data/lib/mail/values/unicode_tables.dat +0 -0
- data/lib/mail/version.rb +3 -2
- data/lib/mail/version_specific/ruby_1_8.rb +50 -6
- data/lib/mail/version_specific/ruby_1_9.rb +103 -18
- data/lib/mail.rb +5 -12
- metadata +33 -57
- data/CHANGELOG.rdoc +0 -759
- data/CONTRIBUTING.md +0 -60
- data/Dependencies.txt +0 -2
- data/Gemfile +0 -15
- data/Rakefile +0 -29
- data/TODO.rdoc +0 -9
- data/lib/mail/core_extensions/nil.rb +0 -19
- data/lib/mail/core_extensions/object.rb +0 -13
- data/lib/mail/core_extensions/string/access.rb +0 -145
- data/lib/mail/core_extensions/string/multibyte.rb +0 -78
- data/lib/mail/multibyte/exceptions.rb +0 -8
- data/lib/mail/parsers/ragel/common.rl +0 -185
- data/lib/mail/parsers/ragel/parser_info.rb +0 -61
- data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +0 -14864
- data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +0 -751
- data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +0 -614
- data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +0 -447
- data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +0 -825
- data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +0 -817
- data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +0 -2149
- data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +0 -1570
- data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +0 -440
- data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +0 -564
- data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +0 -51
- data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +0 -5144
- data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +0 -37
- data/lib/mail/parsers/ragel/ruby.rb +0 -40
- data/lib/mail/parsers/ragel.rb +0 -17
data/lib/mail/multibyte/chars.rb
CHANGED
|
@@ -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:
|
|
@@ -39,7 +41,7 @@ module Mail #:nodoc:
|
|
|
39
41
|
if RUBY_VERSION >= "1.9"
|
|
40
42
|
# Creates a new Chars instance by wrapping _string_.
|
|
41
43
|
def initialize(string)
|
|
42
|
-
@wrapped_string = string
|
|
44
|
+
@wrapped_string = string.dup
|
|
43
45
|
@wrapped_string.force_encoding(Encoding::UTF_8) unless @wrapped_string.frozen?
|
|
44
46
|
end
|
|
45
47
|
else
|
|
@@ -102,7 +104,7 @@ module Mail #:nodoc:
|
|
|
102
104
|
# Returns a new Chars object containing the _other_ object concatenated to the string.
|
|
103
105
|
#
|
|
104
106
|
# Example:
|
|
105
|
-
# ('Café'
|
|
107
|
+
# (Mail::Multibyte.mb_chars('Café') + ' périferôl').to_s # => "Café périferôl"
|
|
106
108
|
def +(other)
|
|
107
109
|
chars(@wrapped_string + other)
|
|
108
110
|
end
|
|
@@ -110,7 +112,7 @@ module Mail #:nodoc:
|
|
|
110
112
|
# Like <tt>String#=~</tt> only it returns the character offset (in codepoints) instead of the byte offset.
|
|
111
113
|
#
|
|
112
114
|
# Example:
|
|
113
|
-
# 'Café périferôl'
|
|
115
|
+
# Mail::Multibyte.mb_chars('Café périferôl') =~ /ô/ # => 12
|
|
114
116
|
def =~(other)
|
|
115
117
|
translate_offset(@wrapped_string =~ other)
|
|
116
118
|
end
|
|
@@ -118,7 +120,7 @@ module Mail #:nodoc:
|
|
|
118
120
|
# Inserts the passed string at specified codepoint offsets.
|
|
119
121
|
#
|
|
120
122
|
# Example:
|
|
121
|
-
# 'Café'.
|
|
123
|
+
# Mail::Multibyte.mb_chars('Café').insert(4, ' périferôl').to_s # => "Café périferôl"
|
|
122
124
|
def insert(offset, fragment)
|
|
123
125
|
unpacked = Unicode.u_unpack(@wrapped_string)
|
|
124
126
|
unless offset > unpacked.length
|
|
@@ -134,7 +136,7 @@ module Mail #:nodoc:
|
|
|
134
136
|
# Returns +true+ if contained string contains _other_. Returns +false+ otherwise.
|
|
135
137
|
#
|
|
136
138
|
# Example:
|
|
137
|
-
# 'Café'.
|
|
139
|
+
# Mail::Multibyte.mb_chars('Café').include?('é') # => true
|
|
138
140
|
def include?(other)
|
|
139
141
|
# We have to redefine this method because Enumerable defines it.
|
|
140
142
|
@wrapped_string.include?(other)
|
|
@@ -143,8 +145,8 @@ module Mail #:nodoc:
|
|
|
143
145
|
# Returns the position _needle_ in the string, counting in codepoints. Returns +nil+ if _needle_ isn't found.
|
|
144
146
|
#
|
|
145
147
|
# Example:
|
|
146
|
-
# 'Café périferôl'.
|
|
147
|
-
# 'Café périferôl'.
|
|
148
|
+
# Mail::Multibyte.mb_chars('Café périferôl').index('ô') # => 12
|
|
149
|
+
# Mail::Multibyte.mb_chars('Café périferôl').index(/\w/u) # => 0
|
|
148
150
|
def index(needle, offset=0)
|
|
149
151
|
wrapped_offset = first(offset).wrapped_string.length
|
|
150
152
|
index = @wrapped_string.index(needle, wrapped_offset)
|
|
@@ -156,8 +158,8 @@ module Mail #:nodoc:
|
|
|
156
158
|
# string. Returns +nil+ if _needle_ isn't found.
|
|
157
159
|
#
|
|
158
160
|
# Example:
|
|
159
|
-
# 'Café périferôl'.
|
|
160
|
-
# 'Café périferôl'.
|
|
161
|
+
# Mail::Multibyte.mb_chars('Café périferôl').rindex('é') # => 6
|
|
162
|
+
# Mail::Multibyte.mb_chars('Café périferôl').rindex(/\w/u) # => 13
|
|
161
163
|
def rindex(needle, offset=nil)
|
|
162
164
|
offset ||= length
|
|
163
165
|
wrapped_offset = first(offset).wrapped_string.length
|
|
@@ -189,7 +191,7 @@ module Mail #:nodoc:
|
|
|
189
191
|
# Returns the codepoint of the first character in the string.
|
|
190
192
|
#
|
|
191
193
|
# Example:
|
|
192
|
-
# 'こんにちは'.
|
|
194
|
+
# Mail::Multibyte.mb_chars('こんにちは').ord # => 12371
|
|
193
195
|
def ord
|
|
194
196
|
Unicode.u_unpack(@wrapped_string)[0]
|
|
195
197
|
end
|
|
@@ -198,10 +200,10 @@ module Mail #:nodoc:
|
|
|
198
200
|
#
|
|
199
201
|
# Example:
|
|
200
202
|
#
|
|
201
|
-
# "¾ cup".
|
|
203
|
+
# Mail::Multibyte.mb_chars("¾ cup").rjust(8).to_s
|
|
202
204
|
# # => " ¾ cup"
|
|
203
205
|
#
|
|
204
|
-
# "¾ cup".
|
|
206
|
+
# Mail::Multibyte.mb_chars("¾ cup").rjust(8, " ").to_s # Use non-breaking whitespace
|
|
205
207
|
# # => " ¾ cup"
|
|
206
208
|
def rjust(integer, padstr=' ')
|
|
207
209
|
justify(integer, :right, padstr)
|
|
@@ -211,10 +213,10 @@ module Mail #:nodoc:
|
|
|
211
213
|
#
|
|
212
214
|
# Example:
|
|
213
215
|
#
|
|
214
|
-
# "¾ cup".
|
|
216
|
+
# Mail::Multibyte.mb_chars("¾ cup").rjust(8).to_s
|
|
215
217
|
# # => "¾ cup "
|
|
216
218
|
#
|
|
217
|
-
# "¾ cup".
|
|
219
|
+
# Mail::Multibyte.mb_chars("¾ cup").rjust(8, " ").to_s # Use non-breaking whitespace
|
|
218
220
|
# # => "¾ cup "
|
|
219
221
|
def ljust(integer, padstr=' ')
|
|
220
222
|
justify(integer, :left, padstr)
|
|
@@ -224,10 +226,10 @@ module Mail #:nodoc:
|
|
|
224
226
|
#
|
|
225
227
|
# Example:
|
|
226
228
|
#
|
|
227
|
-
# "¾ cup".
|
|
229
|
+
# Mail::Multibyte.mb_chars("¾ cup").center(8).to_s
|
|
228
230
|
# # => " ¾ cup "
|
|
229
231
|
#
|
|
230
|
-
# "¾ cup".
|
|
232
|
+
# Mail::Multibyte.mb_chars("¾ cup").center(8, " ").to_s # Use non-breaking whitespace
|
|
231
233
|
# # => " ¾ cup "
|
|
232
234
|
def center(integer, padstr=' ')
|
|
233
235
|
justify(integer, :center, padstr)
|
|
@@ -243,7 +245,7 @@ module Mail #:nodoc:
|
|
|
243
245
|
# instances instead of String. This makes chaining methods easier.
|
|
244
246
|
#
|
|
245
247
|
# Example:
|
|
246
|
-
# 'Café périferôl'.
|
|
248
|
+
# Mail::Multibyte.mb_chars('Café périferôl').split(/é/).map { |part| part.upcase.to_s } # => ["CAF", " P", "RIFERÔL"]
|
|
247
249
|
def split(*args)
|
|
248
250
|
@wrapped_string.split(*args).map { |i| i.mb_chars }
|
|
249
251
|
end
|
|
@@ -268,12 +270,12 @@ module Mail #:nodoc:
|
|
|
268
270
|
@wrapped_string[*args] = replace_by
|
|
269
271
|
else
|
|
270
272
|
result = Unicode.u_unpack(@wrapped_string)
|
|
271
|
-
if args[0].is_a?(
|
|
273
|
+
if args[0].is_a?(Integer)
|
|
272
274
|
raise IndexError, "index #{args[0]} out of string" if args[0] >= result.length
|
|
273
275
|
min = args[0]
|
|
274
276
|
max = args[1].nil? ? min : (min + args[1] - 1)
|
|
275
277
|
range = Range.new(min, max)
|
|
276
|
-
replace_by = [replace_by].pack('U') if replace_by.is_a?(
|
|
278
|
+
replace_by = [replace_by].pack('U') if replace_by.is_a?(Integer)
|
|
277
279
|
elsif args.first.is_a?(Range)
|
|
278
280
|
raise RangeError, "#{args[0]} out of range" if args[0].min >= result.length
|
|
279
281
|
range = args[0]
|
|
@@ -291,7 +293,7 @@ module Mail #:nodoc:
|
|
|
291
293
|
# Reverses all characters in the string.
|
|
292
294
|
#
|
|
293
295
|
# Example:
|
|
294
|
-
# 'Café'.
|
|
296
|
+
# Mail::Multibyte.mb_chars('Café').reverse.to_s # => 'éfaC'
|
|
295
297
|
def reverse
|
|
296
298
|
chars(Unicode.g_unpack(@wrapped_string).reverse.flatten.pack('U*'))
|
|
297
299
|
end
|
|
@@ -300,7 +302,7 @@ module Mail #:nodoc:
|
|
|
300
302
|
# character.
|
|
301
303
|
#
|
|
302
304
|
# Example:
|
|
303
|
-
# 'こんにちは'.
|
|
305
|
+
# Mail::Multibyte.mb_chars('こんにちは').slice(2..3).to_s # => "にち"
|
|
304
306
|
def slice(*args)
|
|
305
307
|
if args.size > 2
|
|
306
308
|
raise ArgumentError, "wrong number of arguments (#{args.size} for 1)" # Do as if we were native
|
|
@@ -337,7 +339,7 @@ module Mail #:nodoc:
|
|
|
337
339
|
# Convert characters in the string to uppercase.
|
|
338
340
|
#
|
|
339
341
|
# Example:
|
|
340
|
-
# 'Laurent, où sont les tests ?'.
|
|
342
|
+
# Mail::Multibyte.mb_chars('Laurent, où sont les tests ?').upcase.to_s # => "LAURENT, OÙ SONT LES TESTS ?"
|
|
341
343
|
def upcase
|
|
342
344
|
chars(Unicode.apply_mapping(@wrapped_string, :uppercase_mapping))
|
|
343
345
|
end
|
|
@@ -345,7 +347,7 @@ module Mail #:nodoc:
|
|
|
345
347
|
# Convert characters in the string to lowercase.
|
|
346
348
|
#
|
|
347
349
|
# Example:
|
|
348
|
-
# 'VĚDA A VÝZKUM'.
|
|
350
|
+
# Mail::Multibyte.mb_chars('VĚDA A VÝZKUM').downcase.to_s # => "věda a výzkum"
|
|
349
351
|
def downcase
|
|
350
352
|
chars(Unicode.apply_mapping(@wrapped_string, :lowercase_mapping))
|
|
351
353
|
end
|
|
@@ -353,7 +355,7 @@ module Mail #:nodoc:
|
|
|
353
355
|
# Converts the first character to uppercase and the remainder to lowercase.
|
|
354
356
|
#
|
|
355
357
|
# Example:
|
|
356
|
-
# 'über'.
|
|
358
|
+
# Mail::Multibyte.mb_chars('über').capitalize.to_s # => "Über"
|
|
357
359
|
def capitalize
|
|
358
360
|
(slice(0) || chars('')).upcase + (slice(1..-1) || chars('')).downcase
|
|
359
361
|
end
|
|
@@ -361,8 +363,8 @@ module Mail #:nodoc:
|
|
|
361
363
|
# Capitalizes the first letter of every word, when possible.
|
|
362
364
|
#
|
|
363
365
|
# Example:
|
|
364
|
-
# "ÉL QUE SE ENTERÓ".
|
|
365
|
-
# "日本語".
|
|
366
|
+
# Mail::Multibyte.mb_chars("ÉL QUE SE ENTERÓ").titleize # => "Él Que Se Enteró"
|
|
367
|
+
# Mail::Multibyte.mb_chars("日本語").titleize # => "日本語"
|
|
366
368
|
def titleize
|
|
367
369
|
chars(downcase.to_s.gsub(/\b('?\S)/u) { Unicode.apply_mapping $1, :uppercase_mapping })
|
|
368
370
|
end
|
|
@@ -382,7 +384,7 @@ module Mail #:nodoc:
|
|
|
382
384
|
#
|
|
383
385
|
# Example:
|
|
384
386
|
# 'é'.length # => 2
|
|
385
|
-
# 'é'.
|
|
387
|
+
# Mail::Multibyte.mb_chars('é').decompose.to_s.length # => 3
|
|
386
388
|
def decompose
|
|
387
389
|
chars(Unicode.decompose_codepoints(:canonical, Unicode.u_unpack(@wrapped_string)).pack('U*'))
|
|
388
390
|
end
|
|
@@ -391,7 +393,7 @@ module Mail #:nodoc:
|
|
|
391
393
|
#
|
|
392
394
|
# Example:
|
|
393
395
|
# 'é'.length # => 3
|
|
394
|
-
# 'é'.
|
|
396
|
+
# Mail::Multibyte.mb_chars('é').compose.to_s.length # => 2
|
|
395
397
|
def compose
|
|
396
398
|
chars(Unicode.compose_codepoints(Unicode.u_unpack(@wrapped_string)).pack('U*'))
|
|
397
399
|
end
|
|
@@ -399,8 +401,8 @@ module Mail #:nodoc:
|
|
|
399
401
|
# Returns the number of grapheme clusters in the string.
|
|
400
402
|
#
|
|
401
403
|
# Example:
|
|
402
|
-
# 'क्षि'.
|
|
403
|
-
# 'क्षि'.
|
|
404
|
+
# Mail::Multibyte.mb_chars('क्षि').length # => 4
|
|
405
|
+
# Mail::Multibyte.mb_chars('क्षि').g_length # => 3
|
|
404
406
|
def g_length
|
|
405
407
|
Unicode.g_unpack(@wrapped_string).length
|
|
406
408
|
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
|
#
|
|
@@ -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
|
|
@@ -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
|
data/lib/mail/multibyte/utils.rb
CHANGED
data/lib/mail/multibyte.rb
CHANGED
|
@@ -1,23 +1,73 @@
|
|
|
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
|
-
|
|
5
|
-
|
|
6
|
-
require 'mail/multibyte/unicode'
|
|
7
|
+
# Raised when a problem with the encoding was found.
|
|
8
|
+
class EncodingError < StandardError; end
|
|
7
9
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
self.proxy_class = Mail::Multibyte::Chars
|
|
21
|
+
|
|
22
|
+
if RUBY_VERSION >= "1.9"
|
|
23
|
+
# == Multibyte proxy
|
|
24
|
+
#
|
|
25
|
+
# +mb_chars+ is a multibyte safe proxy for string methods.
|
|
26
|
+
#
|
|
27
|
+
# In Ruby 1.8 and older it creates and returns an instance of the Mail::Multibyte::Chars class which
|
|
28
|
+
# encapsulates the original string. A Unicode safe version of all the String methods are defined on this proxy
|
|
29
|
+
# class. If the proxy class doesn't respond to a certain method, it's forwarded to the encapsuled string.
|
|
30
|
+
#
|
|
31
|
+
# name = 'Claus Müller'
|
|
32
|
+
# name.reverse # => "rell??M sualC"
|
|
33
|
+
# name.length # => 13
|
|
34
|
+
#
|
|
35
|
+
# name.mb_chars.reverse.to_s # => "rellüM sualC"
|
|
36
|
+
# name.mb_chars.length # => 12
|
|
37
|
+
#
|
|
38
|
+
# In Ruby 1.9 and newer +mb_chars+ returns +self+ because String is (mostly) encoding aware. This means that
|
|
39
|
+
# it becomes easy to run one version of your code on multiple Ruby versions.
|
|
40
|
+
#
|
|
41
|
+
# == Method chaining
|
|
42
|
+
#
|
|
43
|
+
# All the methods on the Chars proxy which normally return a string will return a Chars object. This allows
|
|
44
|
+
# method chaining on the result of any of these methods.
|
|
45
|
+
#
|
|
46
|
+
# name.mb_chars.reverse.length # => 12
|
|
47
|
+
#
|
|
48
|
+
# == Interoperability and configuration
|
|
49
|
+
#
|
|
50
|
+
# The Chars object tries to be as interchangeable with String objects as possible: sorting and comparing between
|
|
51
|
+
# String and Char work like expected. The bang! methods change the internal string representation in the Chars
|
|
52
|
+
# object. Interoperability problems can be resolved easily with a +to_s+ call.
|
|
53
|
+
#
|
|
54
|
+
# For more information about the methods defined on the Chars proxy see Mail::Multibyte::Chars. For
|
|
55
|
+
# information about how to change the default Multibyte behaviour see Mail::Multibyte.
|
|
56
|
+
def self.mb_chars(str)
|
|
57
|
+
if proxy_class.consumes?(str)
|
|
58
|
+
proxy_class.new(str)
|
|
59
|
+
else
|
|
60
|
+
str
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
else
|
|
64
|
+
def self.mb_chars(str)
|
|
65
|
+
if proxy_class.wants?(str)
|
|
66
|
+
proxy_class.new(str)
|
|
67
|
+
else
|
|
68
|
+
str
|
|
69
|
+
end
|
|
70
|
+
end
|
|
21
71
|
end
|
|
22
72
|
|
|
23
73
|
# Regular expressions that describe valid byte sequences for a character
|
|
@@ -39,4 +89,4 @@ module Mail #:nodoc:
|
|
|
39
89
|
end
|
|
40
90
|
end
|
|
41
91
|
|
|
42
|
-
require 'mail/multibyte/utils'
|
|
92
|
+
require 'mail/multibyte/utils'
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
module Mail
|
|
2
3
|
|
|
3
4
|
# A delivery method implementation which sends via exim.
|
|
@@ -36,17 +37,13 @@ module Mail
|
|
|
36
37
|
#
|
|
37
38
|
# mail.deliver!
|
|
38
39
|
class Exim < Sendmail
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
DEFAULTS = {
|
|
41
|
+
:location => '/usr/sbin/exim',
|
|
42
|
+
:arguments => '-i -t'
|
|
43
|
+
}
|
|
43
44
|
|
|
44
|
-
def self.call(path, arguments, destinations,
|
|
45
|
-
|
|
46
|
-
io.puts mail.encoded.to_lf
|
|
47
|
-
io.flush
|
|
48
|
-
end
|
|
45
|
+
def self.call(path, arguments, destinations, encoded_message)
|
|
46
|
+
super path, arguments, nil, encoded_message
|
|
49
47
|
end
|
|
50
|
-
|
|
51
48
|
end
|
|
52
49
|
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
require 'mail/check_delivery_params'
|
|
2
3
|
|
|
3
4
|
module Mail
|
|
4
|
-
|
|
5
5
|
# FileDelivery class delivers emails into multiple files based on the destination
|
|
6
6
|
# address. Each file is appended to if it already exists.
|
|
7
7
|
#
|
|
@@ -13,22 +13,20 @@ module Mail
|
|
|
13
13
|
# Make sure the path you specify with :location is writable by the Ruby process
|
|
14
14
|
# running Mail.
|
|
15
15
|
class FileDelivery
|
|
16
|
-
include Mail::CheckDeliveryParams
|
|
17
|
-
|
|
18
16
|
if RUBY_VERSION >= '1.9.1'
|
|
19
17
|
require 'fileutils'
|
|
20
18
|
else
|
|
21
19
|
require 'ftools'
|
|
22
20
|
end
|
|
23
21
|
|
|
22
|
+
attr_accessor :settings
|
|
23
|
+
|
|
24
24
|
def initialize(values)
|
|
25
25
|
self.settings = { :location => './mails' }.merge!(values)
|
|
26
26
|
end
|
|
27
|
-
|
|
28
|
-
attr_accessor :settings
|
|
29
|
-
|
|
27
|
+
|
|
30
28
|
def deliver!(mail)
|
|
31
|
-
|
|
29
|
+
Mail::CheckDeliveryParams.check(mail)
|
|
32
30
|
|
|
33
31
|
if ::File.respond_to?(:makedirs)
|
|
34
32
|
::File.makedirs settings[:location]
|
|
@@ -40,6 +38,5 @@ module Mail
|
|
|
40
38
|
::File.open(::File.join(settings[:location], File.basename(to.to_s)), 'a') { |f| "#{f.write(mail.encoded)}\r\n\r\n" }
|
|
41
39
|
end
|
|
42
40
|
end
|
|
43
|
-
|
|
44
41
|
end
|
|
45
42
|
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require 'mail/check_delivery_params'
|
|
2
|
+
|
|
3
|
+
module Mail
|
|
4
|
+
class LoggerDelivery
|
|
5
|
+
include Mail::CheckDeliveryParams
|
|
6
|
+
|
|
7
|
+
attr_reader :logger, :severity, :settings
|
|
8
|
+
|
|
9
|
+
def initialize(settings)
|
|
10
|
+
@settings = settings
|
|
11
|
+
@logger = settings.fetch(:logger) { default_logger }
|
|
12
|
+
@severity = derive_severity(settings[:severity])
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def deliver!(mail)
|
|
16
|
+
Mail::CheckDeliveryParams.check(mail)
|
|
17
|
+
logger.log(severity) { mail.encoded }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
def default_logger
|
|
22
|
+
require 'logger'
|
|
23
|
+
::Logger.new($stdout)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def derive_severity(severity)
|
|
27
|
+
case severity
|
|
28
|
+
when nil
|
|
29
|
+
Logger::INFO
|
|
30
|
+
when Integer
|
|
31
|
+
severity
|
|
32
|
+
else
|
|
33
|
+
Logger.const_get(severity.to_s.upcase)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
1
2
|
require 'mail/check_delivery_params'
|
|
2
3
|
|
|
3
4
|
module Mail
|
|
@@ -37,19 +38,21 @@ module Mail
|
|
|
37
38
|
#
|
|
38
39
|
# mail.deliver!
|
|
39
40
|
class Sendmail
|
|
40
|
-
|
|
41
|
+
DEFAULTS = {
|
|
42
|
+
:location => '/usr/sbin/sendmail',
|
|
43
|
+
:arguments => '-i'
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
attr_accessor :settings
|
|
41
47
|
|
|
42
48
|
def initialize(values)
|
|
43
|
-
self.settings =
|
|
44
|
-
:arguments => '-i' }.merge(values)
|
|
49
|
+
self.settings = self.class::DEFAULTS.merge(values)
|
|
45
50
|
end
|
|
46
51
|
|
|
47
|
-
attr_accessor :settings
|
|
48
|
-
|
|
49
52
|
def deliver!(mail)
|
|
50
|
-
smtp_from, smtp_to, message =
|
|
53
|
+
smtp_from, smtp_to, message = Mail::CheckDeliveryParams.check(mail)
|
|
51
54
|
|
|
52
|
-
from = "-f #{self.class.shellquote(smtp_from)}"
|
|
55
|
+
from = "-f #{self.class.shellquote(smtp_from)}" if smtp_from
|
|
53
56
|
to = smtp_to.map { |_to| self.class.shellquote(_to) }.join(' ')
|
|
54
57
|
|
|
55
58
|
arguments = "#{settings[:arguments]} #{from} --"
|
|
@@ -58,7 +61,7 @@ module Mail
|
|
|
58
61
|
|
|
59
62
|
def self.call(path, arguments, destinations, encoded_message)
|
|
60
63
|
popen "#{path} #{arguments} #{destinations}" do |io|
|
|
61
|
-
io.puts encoded_message
|
|
64
|
+
io.puts ::Mail::Utilities.binary_unsafe_to_lf(encoded_message)
|
|
62
65
|
io.flush
|
|
63
66
|
end
|
|
64
67
|
end
|
|
@@ -74,15 +77,18 @@ module Mail
|
|
|
74
77
|
end
|
|
75
78
|
|
|
76
79
|
# The following is an adaptation of ruby 1.9.2's shellwords.rb file,
|
|
77
|
-
#
|
|
78
|
-
#
|
|
80
|
+
# with the following modifications:
|
|
81
|
+
#
|
|
82
|
+
# - Wraps in double quotes
|
|
83
|
+
# - Allows '+' to accept email addresses with them
|
|
84
|
+
# - Allows '~' as it is not unescaped in double quotes
|
|
79
85
|
def self.shellquote(address)
|
|
80
86
|
# Process as a single byte sequence because not all shell
|
|
81
87
|
# implementations are multibyte aware.
|
|
82
88
|
#
|
|
83
89
|
# A LF cannot be escaped with a backslash because a backslash + LF
|
|
84
90
|
# combo is regarded as line continuation and simply ignored. Strip it.
|
|
85
|
-
escaped = address.gsub(/([^A-Za-z0-9_\s
|
|
91
|
+
escaped = address.gsub(/([^A-Za-z0-9_\s\+\-.,:\/@~])/n, "\\\\\\1").gsub("\n", '')
|
|
86
92
|
%("#{escaped}")
|
|
87
93
|
end
|
|
88
94
|
end
|