mail 2.6.1 → 2.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/MIT-LICENSE +1 -1
- data/README.md +150 -107
- data/lib/mail/attachments_list.rb +13 -10
- data/lib/mail/body.rb +104 -90
- data/lib/mail/check_delivery_params.rb +55 -10
- data/lib/mail/configuration.rb +3 -0
- data/lib/mail/constants.rb +79 -0
- data/lib/mail/elements/address.rb +96 -108
- data/lib/mail/elements/address_list.rb +13 -30
- data/lib/mail/elements/content_disposition_element.rb +10 -16
- data/lib/mail/elements/content_location_element.rb +9 -13
- data/lib/mail/elements/content_transfer_encoding_element.rb +7 -11
- data/lib/mail/elements/content_type_element.rb +17 -23
- data/lib/mail/elements/date_time_element.rb +8 -15
- data/lib/mail/elements/envelope_from_element.rb +23 -23
- data/lib/mail/elements/message_ids_element.rb +22 -15
- data/lib/mail/elements/mime_version_element.rb +8 -15
- data/lib/mail/elements/phrase_list.rb +13 -10
- data/lib/mail/elements/received_element.rb +28 -19
- data/lib/mail/elements.rb +1 -0
- data/lib/mail/encodings/7bit.rb +10 -14
- 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 +20 -0
- data/lib/mail/encodings.rb +102 -92
- data/lib/mail/envelope.rb +12 -14
- data/lib/mail/field.rb +121 -85
- data/lib/mail/field_list.rb +62 -8
- data/lib/mail/fields/bcc_field.rb +42 -48
- data/lib/mail/fields/cc_field.rb +29 -50
- data/lib/mail/fields/comments_field.rb +28 -37
- data/lib/mail/fields/common_address_field.rb +170 -0
- data/lib/mail/fields/common_date_field.rb +58 -0
- data/lib/mail/fields/common_field.rb +77 -0
- data/lib/mail/fields/common_message_id_field.rb +42 -0
- data/lib/mail/fields/content_description_field.rb +8 -14
- data/lib/mail/fields/content_disposition_field.rb +20 -44
- data/lib/mail/fields/content_id_field.rb +25 -51
- data/lib/mail/fields/content_location_field.rb +12 -25
- data/lib/mail/fields/content_transfer_encoding_field.rb +32 -31
- data/lib/mail/fields/content_type_field.rb +51 -80
- data/lib/mail/fields/date_field.rb +24 -52
- data/lib/mail/fields/from_field.rb +29 -50
- data/lib/mail/fields/in_reply_to_field.rb +39 -49
- data/lib/mail/fields/keywords_field.rb +19 -32
- data/lib/mail/fields/message_id_field.rb +26 -71
- data/lib/mail/fields/mime_version_field.rb +20 -30
- data/lib/mail/fields/named_structured_field.rb +11 -0
- data/lib/mail/fields/named_unstructured_field.rb +11 -0
- data/lib/mail/fields/optional_field.rb +10 -7
- data/lib/mail/fields/{common/parameter_hash.rb → parameter_hash.rb} +16 -13
- data/lib/mail/fields/received_field.rb +44 -57
- data/lib/mail/fields/references_field.rb +36 -49
- data/lib/mail/fields/reply_to_field.rb +29 -50
- data/lib/mail/fields/resent_bcc_field.rb +29 -50
- data/lib/mail/fields/resent_cc_field.rb +29 -50
- data/lib/mail/fields/resent_date_field.rb +6 -30
- data/lib/mail/fields/resent_from_field.rb +29 -50
- data/lib/mail/fields/resent_message_id_field.rb +6 -29
- data/lib/mail/fields/resent_sender_field.rb +28 -57
- data/lib/mail/fields/resent_to_field.rb +29 -50
- data/lib/mail/fields/return_path_field.rb +51 -55
- data/lib/mail/fields/sender_field.rb +35 -56
- data/lib/mail/fields/structured_field.rb +4 -30
- data/lib/mail/fields/subject_field.rb +10 -11
- data/lib/mail/fields/to_field.rb +29 -50
- data/lib/mail/fields/unstructured_field.rb +36 -50
- data/lib/mail/fields.rb +1 -0
- data/lib/mail/header.rb +73 -110
- data/lib/mail/indifferent_hash.rb +1 -0
- data/lib/mail/mail.rb +6 -11
- data/lib/mail/matchers/attachment_matchers.rb +44 -0
- data/lib/mail/matchers/has_sent_mail.rb +53 -9
- data/lib/mail/message.rb +132 -136
- data/lib/mail/multibyte/chars.rb +24 -180
- data/lib/mail/multibyte/unicode.rb +31 -26
- data/lib/mail/multibyte/utils.rb +27 -43
- data/lib/mail/multibyte.rb +56 -16
- data/lib/mail/network/delivery_methods/exim.rb +9 -11
- data/lib/mail/network/delivery_methods/file_delivery.rb +14 -16
- data/lib/mail/network/delivery_methods/logger_delivery.rb +34 -0
- data/lib/mail/network/delivery_methods/sendmail.rb +68 -24
- data/lib/mail/network/delivery_methods/smtp.rb +77 -54
- data/lib/mail/network/delivery_methods/smtp_connection.rb +5 -9
- data/lib/mail/network/delivery_methods/test_mailer.rb +9 -9
- data/lib/mail/network/retriever_methods/base.rb +9 -8
- data/lib/mail/network/retriever_methods/imap.rb +21 -7
- data/lib/mail/network/retriever_methods/pop3.rb +6 -3
- data/lib/mail/network/retriever_methods/test_retriever.rb +4 -2
- data/lib/mail/network.rb +2 -0
- data/lib/mail/parser_tools.rb +15 -0
- data/lib/mail/parsers/address_lists_parser.rb +33226 -116
- data/lib/mail/parsers/address_lists_parser.rl +179 -0
- data/lib/mail/parsers/content_disposition_parser.rb +883 -49
- data/lib/mail/parsers/content_disposition_parser.rl +89 -0
- data/lib/mail/parsers/content_location_parser.rb +810 -23
- data/lib/mail/parsers/content_location_parser.rl +78 -0
- data/lib/mail/parsers/content_transfer_encoding_parser.rb +510 -21
- data/lib/mail/parsers/content_transfer_encoding_parser.rl +71 -0
- data/lib/mail/parsers/content_type_parser.rb +1031 -47
- data/lib/mail/parsers/content_type_parser.rl +90 -0
- data/lib/mail/parsers/date_time_parser.rb +879 -24
- data/lib/mail/parsers/date_time_parser.rl +69 -0
- data/lib/mail/parsers/envelope_from_parser.rb +3670 -40
- data/lib/mail/parsers/envelope_from_parser.rl +89 -0
- data/lib/mail/parsers/message_ids_parser.rb +5147 -25
- data/lib/mail/parsers/message_ids_parser.rl +93 -0
- data/lib/mail/parsers/mime_version_parser.rb +498 -26
- data/lib/mail/parsers/mime_version_parser.rl +68 -0
- data/lib/mail/parsers/phrase_lists_parser.rb +872 -21
- data/lib/mail/parsers/phrase_lists_parser.rl +90 -0
- data/lib/mail/parsers/received_parser.rb +8777 -42
- data/lib/mail/parsers/received_parser.rl +91 -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 +74 -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 +12 -25
- data/lib/mail/part.rb +11 -12
- data/lib/mail/parts_list.rb +88 -14
- data/lib/mail/smtp_envelope.rb +57 -0
- data/lib/mail/utilities.rb +377 -40
- data/lib/mail/values/unicode_tables.dat +0 -0
- data/lib/mail/version.rb +8 -15
- data/lib/mail/yaml.rb +30 -0
- data/lib/mail.rb +9 -32
- metadata +138 -94
- data/CHANGELOG.rdoc +0 -752
- data/CONTRIBUTING.md +0 -60
- data/Dependencies.txt +0 -2
- data/Gemfile +0 -15
- data/Rakefile +0 -29
- data/TODO.rdoc +0 -9
- data/VERSION +0 -4
- data/lib/mail/core_extensions/nil.rb +0 -19
- data/lib/mail/core_extensions/object.rb +0 -13
- data/lib/mail/core_extensions/smtp.rb +0 -24
- data/lib/mail/core_extensions/string/access.rb +0 -145
- data/lib/mail/core_extensions/string/multibyte.rb +0 -78
- data/lib/mail/core_extensions/string.rb +0 -43
- data/lib/mail/fields/common/address_container.rb +0 -16
- data/lib/mail/fields/common/common_address.rb +0 -135
- data/lib/mail/fields/common/common_date.rb +0 -35
- data/lib/mail/fields/common/common_field.rb +0 -57
- data/lib/mail/fields/common/common_message_id.rb +0 -48
- data/lib/mail/multibyte/exceptions.rb +0 -8
- data/lib/mail/parsers/ragel/common.rl +0 -184
- 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 -2129
- 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 -39
- data/lib/mail/parsers/ragel.rb +0 -17
- data/lib/mail/patterns.rb +0 -37
- data/lib/mail/version_specific/ruby_1_8.rb +0 -119
- data/lib/mail/version_specific/ruby_1_9.rb +0 -159
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:
|
@@ -36,16 +38,10 @@ module Mail #:nodoc:
|
|
36
38
|
alias to_s wrapped_string
|
37
39
|
alias to_str wrapped_string
|
38
40
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
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
|
-
|
96
|
-
|
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'.
|
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?(
|
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?(
|
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é'.
|
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
|
-
# 'こんにちは'.
|
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 ?'.
|
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'.
|
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'.
|
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Ó".
|
365
|
-
# "日本語".
|
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
|
-
# 'é'.
|
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
|
-
# 'é'.
|
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
|
-
# 'क्षि'.
|
403
|
-
# 'क्षि'.
|
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(
|
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
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
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
|
@@ -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
@@ -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
|
-
|
6
|
-
|
7
|
-
|
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
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
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
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
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
|
data/lib/mail/multibyte.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
21
|
-
|
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
|
-
|
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,
|
@@ -36,17 +37,14 @@ 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 => %w[ -i -t ]
|
43
|
+
}
|
43
44
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
io.flush
|
48
|
-
end
|
45
|
+
# Uses -t option to extract recipients from the message.
|
46
|
+
def destinations_for(envelope)
|
47
|
+
nil
|
49
48
|
end
|
50
|
-
|
51
49
|
end
|
52
50
|
end
|
@@ -1,7 +1,7 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'mail/smtp_envelope'
|
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,16 @@ 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
|
-
|
16
|
+
require 'fileutils'
|
17
17
|
|
18
|
-
|
19
|
-
require 'fileutils'
|
20
|
-
else
|
21
|
-
require 'ftools'
|
22
|
-
end
|
18
|
+
attr_accessor :settings
|
23
19
|
|
24
20
|
def initialize(values)
|
25
|
-
self.settings = { :location => './mails' }.merge!(values)
|
21
|
+
self.settings = { :location => './mails', :extension => '' }.merge!(values)
|
26
22
|
end
|
27
|
-
|
28
|
-
attr_accessor :settings
|
29
|
-
|
23
|
+
|
30
24
|
def deliver!(mail)
|
31
|
-
|
25
|
+
envelope = Mail::SmtpEnvelope.new(mail)
|
32
26
|
|
33
27
|
if ::File.respond_to?(:makedirs)
|
34
28
|
::File.makedirs settings[:location]
|
@@ -36,10 +30,14 @@ module Mail
|
|
36
30
|
::FileUtils.mkdir_p settings[:location]
|
37
31
|
end
|
38
32
|
|
39
|
-
|
40
|
-
::File.
|
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
|
41
40
|
end
|
42
41
|
end
|
43
|
-
|
44
42
|
end
|
45
43
|
end
|