mail 2.6.6 → 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 +134 -119
- data/lib/mail/attachments_list.rb +10 -9
- data/lib/mail/body.rb +73 -84
- data/lib/mail/check_delivery_params.rb +28 -21
- data/lib/mail/configuration.rb +2 -0
- data/lib/mail/constants.rb +27 -5
- data/lib/mail/elements/address.rb +53 -47
- data/lib/mail/elements/address_list.rb +11 -19
- data/lib/mail/elements/content_disposition_element.rb +9 -16
- data/lib/mail/elements/content_location_element.rb +6 -11
- data/lib/mail/elements/content_transfer_encoding_element.rb +6 -11
- data/lib/mail/elements/content_type_element.rb +16 -23
- data/lib/mail/elements/date_time_element.rb +7 -15
- data/lib/mail/elements/envelope_from_element.rb +22 -23
- data/lib/mail/elements/message_ids_element.rb +18 -13
- data/lib/mail/elements/mime_version_element.rb +7 -15
- data/lib/mail/elements/phrase_list.rb +12 -10
- data/lib/mail/elements/received_element.rb +27 -19
- data/lib/mail/encodings/7bit.rb +9 -14
- data/lib/mail/encodings/8bit.rb +2 -21
- data/lib/mail/encodings/base64.rb +11 -12
- data/lib/mail/encodings/binary.rb +3 -22
- data/lib/mail/encodings/identity.rb +24 -0
- data/lib/mail/encodings/quoted_printable.rb +6 -6
- data/lib/mail/encodings/transfer_encoding.rb +38 -29
- data/lib/mail/encodings/unix_to_unix.rb +3 -1
- data/lib/mail/encodings.rb +81 -54
- data/lib/mail/envelope.rb +11 -14
- data/lib/mail/field.rb +119 -98
- data/lib/mail/field_list.rb +60 -7
- data/lib/mail/fields/bcc_field.rb +34 -52
- data/lib/mail/fields/cc_field.rb +28 -49
- data/lib/mail/fields/comments_field.rb +27 -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 +7 -14
- data/lib/mail/fields/content_disposition_field.rb +13 -38
- data/lib/mail/fields/content_id_field.rb +24 -51
- data/lib/mail/fields/content_location_field.rb +11 -25
- data/lib/mail/fields/content_transfer_encoding_field.rb +31 -31
- data/lib/mail/fields/content_type_field.rb +50 -80
- data/lib/mail/fields/date_field.rb +23 -52
- data/lib/mail/fields/from_field.rb +28 -49
- data/lib/mail/fields/in_reply_to_field.rb +38 -49
- data/lib/mail/fields/keywords_field.rb +18 -31
- data/lib/mail/fields/message_id_field.rb +25 -71
- data/lib/mail/fields/mime_version_field.rb +19 -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 +9 -7
- data/lib/mail/fields/{common/parameter_hash.rb → parameter_hash.rb} +13 -11
- data/lib/mail/fields/received_field.rb +43 -57
- data/lib/mail/fields/references_field.rb +35 -49
- data/lib/mail/fields/reply_to_field.rb +28 -49
- data/lib/mail/fields/resent_bcc_field.rb +28 -49
- data/lib/mail/fields/resent_cc_field.rb +28 -49
- data/lib/mail/fields/resent_date_field.rb +5 -30
- data/lib/mail/fields/resent_from_field.rb +28 -49
- data/lib/mail/fields/resent_message_id_field.rb +5 -29
- data/lib/mail/fields/resent_sender_field.rb +27 -56
- data/lib/mail/fields/resent_to_field.rb +28 -49
- data/lib/mail/fields/return_path_field.rb +50 -54
- data/lib/mail/fields/sender_field.rb +34 -55
- data/lib/mail/fields/structured_field.rb +3 -30
- data/lib/mail/fields/subject_field.rb +9 -11
- data/lib/mail/fields/to_field.rb +28 -49
- data/lib/mail/fields/unstructured_field.rb +32 -47
- data/lib/mail/header.rb +71 -110
- data/lib/mail/mail.rb +2 -10
- data/lib/mail/matchers/attachment_matchers.rb +15 -0
- data/lib/mail/matchers/has_sent_mail.rb +21 -1
- data/lib/mail/message.rb +113 -117
- data/lib/mail/multibyte/chars.rb +21 -178
- data/lib/mail/multibyte/unicode.rb +10 -10
- data/lib/mail/multibyte/utils.rb +26 -43
- data/lib/mail/multibyte.rb +55 -16
- data/lib/mail/network/delivery_methods/exim.rb +5 -4
- data/lib/mail/network/delivery_methods/file_delivery.rb +11 -10
- data/lib/mail/network/delivery_methods/logger_delivery.rb +34 -0
- data/lib/mail/network/delivery_methods/sendmail.rb +62 -21
- data/lib/mail/network/delivery_methods/smtp.rb +75 -50
- data/lib/mail/network/delivery_methods/smtp_connection.rb +3 -4
- data/lib/mail/network/delivery_methods/test_mailer.rb +4 -2
- data/lib/mail/network/retriever_methods/base.rb +8 -8
- data/lib/mail/network/retriever_methods/imap.rb +20 -7
- data/lib/mail/network/retriever_methods/pop3.rb +5 -3
- data/lib/mail/network/retriever_methods/test_retriever.rb +2 -1
- data/lib/mail/network.rb +1 -0
- data/lib/mail/parser_tools.rb +15 -0
- data/lib/mail/parsers/address_lists_parser.rb +33225 -116
- data/lib/mail/parsers/address_lists_parser.rl +179 -0
- data/lib/mail/parsers/content_disposition_parser.rb +882 -49
- data/lib/mail/parsers/content_disposition_parser.rl +89 -0
- data/lib/mail/parsers/content_location_parser.rb +809 -23
- data/lib/mail/parsers/content_location_parser.rl +78 -0
- data/lib/mail/parsers/content_transfer_encoding_parser.rb +509 -21
- data/lib/mail/parsers/content_transfer_encoding_parser.rl +71 -0
- data/lib/mail/parsers/content_type_parser.rb +1037 -56
- data/lib/mail/parsers/content_type_parser.rl +90 -0
- data/lib/mail/parsers/date_time_parser.rb +877 -25
- data/lib/mail/parsers/date_time_parser.rl +69 -0
- data/lib/mail/parsers/envelope_from_parser.rb +3669 -40
- data/lib/mail/parsers/envelope_from_parser.rl +89 -0
- data/lib/mail/parsers/message_ids_parser.rb +5146 -25
- data/lib/mail/parsers/message_ids_parser.rl +93 -0
- data/lib/mail/parsers/mime_version_parser.rb +497 -26
- data/lib/mail/parsers/mime_version_parser.rl +68 -0
- data/lib/mail/parsers/phrase_lists_parser.rb +870 -22
- data/lib/mail/parsers/phrase_lists_parser.rl +90 -0
- data/lib/mail/parsers/received_parser.rb +8776 -43
- 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 +11 -25
- data/lib/mail/part.rb +6 -10
- data/lib/mail/parts_list.rb +62 -6
- data/lib/mail/smtp_envelope.rb +57 -0
- data/lib/mail/utilities.rb +343 -74
- data/lib/mail/version.rb +2 -2
- data/lib/mail/yaml.rb +30 -0
- data/lib/mail.rb +5 -35
- metadata +111 -66
- data/CHANGELOG.rdoc +0 -803
- data/CONTRIBUTING.md +0 -60
- data/Dependencies.txt +0 -2
- data/Gemfile +0 -14
- data/Rakefile +0 -29
- data/TODO.rdoc +0 -9
- data/lib/mail/core_extensions/smtp.rb +0 -25
- data/lib/mail/core_extensions/string/access.rb +0 -146
- data/lib/mail/core_extensions/string/multibyte.rb +0 -79
- data/lib/mail/core_extensions/string.rb +0 -21
- data/lib/mail/fields/common/address_container.rb +0 -17
- data/lib/mail/fields/common/common_address.rb +0 -136
- data/lib/mail/fields/common/common_date.rb +0 -36
- data/lib/mail/fields/common/common_field.rb +0 -61
- data/lib/mail/fields/common/common_message_id.rb +0 -49
- data/lib/mail/multibyte/exceptions.rb +0 -9
- 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 -18
- data/lib/mail/version_specific/ruby_1_8.rb +0 -126
- data/lib/mail/version_specific/ruby_1_9.rb +0 -226
data/lib/mail/multibyte/chars.rb
CHANGED
@@ -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
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
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
|
-
|
97
|
-
|
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'.
|
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
|
@@ -292,7 +135,7 @@ module Mail #:nodoc:
|
|
292
135
|
# Reverses all characters in the string.
|
293
136
|
#
|
294
137
|
# Example:
|
295
|
-
# 'Café'.
|
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
|
-
# 'こんにちは'.
|
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 ?'.
|
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'.
|
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'.
|
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Ó".
|
366
|
-
# "日本語".
|
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
|
-
# 'é'.
|
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
|
-
# 'é'.
|
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
|
-
# 'क्षि'.
|
404
|
-
# 'क्षि'.
|
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(
|
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
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
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
|
|
data/lib/mail/multibyte/utils.rb
CHANGED
@@ -3,59 +3,42 @@
|
|
3
3
|
|
4
4
|
module Mail #:nodoc:
|
5
5
|
module Multibyte #:nodoc:
|
6
|
-
|
7
|
-
|
8
|
-
|
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
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
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
|
data/lib/mail/multibyte.rb
CHANGED
@@ -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
|
-
|
6
|
-
|
7
|
-
require 'mail/multibyte/unicode'
|
7
|
+
# Raised when a problem with the encoding was found.
|
8
|
+
class EncodingError < StandardError; end
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
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
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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,
|
@@ -39,11 +39,12 @@ module Mail
|
|
39
39
|
class Exim < Sendmail
|
40
40
|
DEFAULTS = {
|
41
41
|
:location => '/usr/sbin/exim',
|
42
|
-
:arguments =>
|
42
|
+
:arguments => %w[ -i -t ]
|
43
43
|
}
|
44
44
|
|
45
|
-
|
46
|
-
|
45
|
+
# Uses -t option to extract recipients from the message.
|
46
|
+
def destinations_for(envelope)
|
47
|
+
nil
|
47
48
|
end
|
48
49
|
end
|
49
50
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require 'mail/
|
2
|
+
require 'mail/smtp_envelope'
|
3
3
|
|
4
4
|
module Mail
|
5
5
|
# FileDelivery class delivers emails into multiple files based on the destination
|
@@ -13,20 +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
|
-
|
17
|
-
require 'fileutils'
|
18
|
-
else
|
19
|
-
require 'ftools'
|
20
|
-
end
|
16
|
+
require 'fileutils'
|
21
17
|
|
22
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
23
|
|
28
24
|
def deliver!(mail)
|
29
|
-
Mail::
|
25
|
+
envelope = Mail::SmtpEnvelope.new(mail)
|
30
26
|
|
31
27
|
if ::File.respond_to?(:makedirs)
|
32
28
|
::File.makedirs settings[:location]
|
@@ -34,8 +30,13 @@ module Mail
|
|
34
30
|
::FileUtils.mkdir_p settings[:location]
|
35
31
|
end
|
36
32
|
|
37
|
-
|
38
|
-
::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
|
39
40
|
end
|
40
41
|
end
|
41
42
|
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
|