mail 2.6.6 → 2.7.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 +74 -90
- data/lib/mail/attachments_list.rb +8 -4
- data/lib/mail/body.rb +50 -38
- data/lib/mail/check_delivery_params.rb +8 -6
- data/lib/mail/configuration.rb +2 -0
- data/lib/mail/constants.rb +1 -1
- data/lib/mail/core_extensions/smtp.rb +19 -16
- data/lib/mail/core_extensions/string.rb +0 -4
- data/lib/mail/elements/address.rb +28 -22
- data/lib/mail/elements/address_list.rb +10 -18
- data/lib/mail/elements/content_disposition_element.rb +8 -15
- data/lib/mail/elements/content_location_element.rb +5 -10
- data/lib/mail/elements/content_transfer_encoding_element.rb +5 -10
- data/lib/mail/elements/content_type_element.rb +8 -19
- data/lib/mail/elements/date_time_element.rb +6 -14
- data/lib/mail/elements/envelope_from_element.rb +14 -21
- data/lib/mail/elements/message_ids_element.rb +8 -12
- data/lib/mail/elements/mime_version_element.rb +6 -14
- data/lib/mail/elements/phrase_list.rb +6 -9
- data/lib/mail/elements/received_element.rb +9 -15
- data/lib/mail/encodings/7bit.rb +5 -15
- 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 +99 -43
- data/lib/mail/envelope.rb +1 -1
- data/lib/mail/field.rb +96 -59
- data/lib/mail/fields/bcc_field.rb +2 -2
- data/lib/mail/fields/cc_field.rb +1 -1
- data/lib/mail/fields/comments_field.rb +1 -1
- data/lib/mail/fields/common/common_address.rb +32 -7
- data/lib/mail/fields/common/common_field.rb +1 -10
- data/lib/mail/fields/common/parameter_hash.rb +1 -1
- data/lib/mail/fields/content_description_field.rb +1 -1
- data/lib/mail/fields/content_disposition_field.rb +3 -3
- data/lib/mail/fields/content_id_field.rb +2 -2
- data/lib/mail/fields/content_location_field.rb +1 -1
- data/lib/mail/fields/content_transfer_encoding_field.rb +1 -1
- data/lib/mail/fields/content_type_field.rb +4 -9
- data/lib/mail/fields/date_field.rb +2 -3
- data/lib/mail/fields/from_field.rb +1 -1
- data/lib/mail/fields/in_reply_to_field.rb +1 -1
- data/lib/mail/fields/keywords_field.rb +1 -1
- data/lib/mail/fields/message_id_field.rb +1 -1
- data/lib/mail/fields/mime_version_field.rb +1 -1
- data/lib/mail/fields/optional_field.rb +4 -1
- data/lib/mail/fields/received_field.rb +1 -1
- data/lib/mail/fields/references_field.rb +1 -1
- data/lib/mail/fields/reply_to_field.rb +1 -1
- data/lib/mail/fields/resent_bcc_field.rb +1 -1
- data/lib/mail/fields/resent_cc_field.rb +1 -1
- data/lib/mail/fields/resent_date_field.rb +0 -1
- data/lib/mail/fields/resent_from_field.rb +1 -1
- data/lib/mail/fields/resent_message_id_field.rb +1 -1
- data/lib/mail/fields/resent_sender_field.rb +1 -1
- data/lib/mail/fields/resent_to_field.rb +1 -1
- data/lib/mail/fields/return_path_field.rb +1 -1
- data/lib/mail/fields/sender_field.rb +1 -1
- data/lib/mail/fields/subject_field.rb +1 -1
- data/lib/mail/fields/to_field.rb +1 -1
- data/lib/mail/fields/unstructured_field.rb +21 -4
- data/lib/mail/header.rb +10 -8
- data/lib/mail/mail.rb +2 -10
- data/lib/mail/matchers/has_sent_mail.rb +21 -1
- data/lib/mail/message.rb +78 -68
- data/lib/mail/multibyte/chars.rb +29 -28
- data/lib/mail/multibyte/unicode.rb +10 -10
- data/lib/mail/multibyte.rb +64 -15
- data/lib/mail/network/delivery_methods/logger_delivery.rb +37 -0
- data/lib/mail/network/delivery_methods/sendmail.rb +8 -5
- data/lib/mail/network/delivery_methods/smtp.rb +58 -49
- data/lib/mail/network/delivery_methods/smtp_connection.rb +9 -1
- data/lib/mail/network/retriever_methods/imap.rb +18 -5
- data/lib/mail/network/retriever_methods/pop3.rb +3 -1
- data/lib/mail/network.rb +1 -0
- data/lib/mail/parser_tools.rb +15 -0
- data/lib/mail/parsers/address_lists_parser.rb +33207 -104
- data/lib/mail/parsers/address_lists_parser.rl +172 -0
- data/lib/mail/parsers/content_disposition_parser.rb +876 -49
- data/lib/mail/parsers/content_disposition_parser.rl +82 -0
- data/lib/mail/parsers/content_location_parser.rb +803 -23
- data/lib/mail/parsers/content_location_parser.rl +71 -0
- data/lib/mail/parsers/content_transfer_encoding_parser.rb +501 -19
- data/lib/mail/parsers/content_transfer_encoding_parser.rl +64 -0
- data/lib/mail/parsers/content_type_parser.rb +1023 -48
- data/lib/mail/parsers/content_type_parser.rl +83 -0
- data/lib/mail/parsers/date_time_parser.rb +870 -24
- data/lib/mail/parsers/date_time_parser.rl +62 -0
- data/lib/mail/parsers/envelope_from_parser.rb +3569 -34
- data/lib/mail/parsers/envelope_from_parser.rl +82 -0
- data/lib/mail/parsers/message_ids_parser.rb +2839 -25
- data/lib/mail/parsers/message_ids_parser.rl +82 -0
- data/lib/mail/parsers/mime_version_parser.rb +491 -26
- data/lib/mail/parsers/mime_version_parser.rl +61 -0
- data/lib/mail/parsers/phrase_lists_parser.rb +860 -18
- data/lib/mail/parsers/phrase_lists_parser.rl +83 -0
- data/lib/mail/parsers/received_parser.rb +8764 -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 +16 -24
- data/lib/mail/part.rb +3 -3
- data/lib/mail/parts_list.rb +5 -6
- data/lib/mail/utilities.rb +59 -28
- data/lib/mail/version.rb +2 -2
- data/lib/mail/version_specific/ruby_1_8.rb +40 -3
- data/lib/mail/version_specific/ruby_1_9.rb +61 -9
- data/lib/mail.rb +3 -16
- metadata +44 -53
- 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/string/access.rb +0 -146
- data/lib/mail/core_extensions/string/multibyte.rb +0 -79
- 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/message.rb
CHANGED
@@ -56,15 +56,23 @@ module Mail
|
|
56
56
|
#
|
57
57
|
# ===Making an email via a block
|
58
58
|
#
|
59
|
-
# mail = Mail.new do
|
60
|
-
#
|
61
|
-
#
|
62
|
-
# subject 'This is a test email'
|
63
|
-
#
|
59
|
+
# mail = Mail.new do |m|
|
60
|
+
# m.from 'mikel@test.lindsaar.net'
|
61
|
+
# m.to 'you@test.lindsaar.net'
|
62
|
+
# m.subject 'This is a test email'
|
63
|
+
# m.body File.read('body.txt')
|
64
64
|
# end
|
65
65
|
#
|
66
66
|
# mail.to_s #=> "From: mikel@test.lindsaar.net\r\nTo: you@...
|
67
67
|
#
|
68
|
+
# If may also pass a block with no arguments, in which case it will
|
69
|
+
# be evaluated in the scope of the new message instance:
|
70
|
+
#
|
71
|
+
# mail = Mail.new do
|
72
|
+
# from 'mikel@test.lindsaar.net'
|
73
|
+
# # …
|
74
|
+
# end
|
75
|
+
#
|
68
76
|
# ===Making an email via passing a string
|
69
77
|
#
|
70
78
|
# mail = Mail.new("To: mikel@test.lindsaar.net\r\nSubject: Hello\r\n\r\nHi there!")
|
@@ -129,8 +137,23 @@ module Mail
|
|
129
137
|
init_with_string(args.flatten[0].to_s)
|
130
138
|
end
|
131
139
|
|
140
|
+
# Support both builder styles:
|
141
|
+
#
|
142
|
+
# Mail.new do
|
143
|
+
# to 'recipient@example.com'
|
144
|
+
# end
|
145
|
+
#
|
146
|
+
# and
|
147
|
+
#
|
148
|
+
# Mail.new do |m|
|
149
|
+
# m.to 'recipient@example.com'
|
150
|
+
# end
|
132
151
|
if block_given?
|
133
|
-
|
152
|
+
if block.arity.zero? || (RUBY_VERSION < '1.9' && block.arity < 1)
|
153
|
+
instance_eval(&block)
|
154
|
+
else
|
155
|
+
yield self
|
156
|
+
end
|
134
157
|
end
|
135
158
|
|
136
159
|
self
|
@@ -213,7 +236,7 @@ module Mail
|
|
213
236
|
self.default_charset = 'UTF-8'
|
214
237
|
|
215
238
|
def register_for_delivery_notification(observer)
|
216
|
-
|
239
|
+
warn("Message#register_for_delivery_notification is deprecated, please call Mail.register_observer instead")
|
217
240
|
Mail.register_observer(observer)
|
218
241
|
end
|
219
242
|
|
@@ -225,7 +248,7 @@ module Mail
|
|
225
248
|
Mail.inform_interceptors(self)
|
226
249
|
end
|
227
250
|
|
228
|
-
# Delivers
|
251
|
+
# Delivers a mail object.
|
229
252
|
#
|
230
253
|
# Examples:
|
231
254
|
#
|
@@ -360,13 +383,8 @@ module Mail
|
|
360
383
|
if self.message_id && other.message_id
|
361
384
|
self.encoded == other.encoded
|
362
385
|
else
|
363
|
-
|
364
|
-
|
365
|
-
self.message_id, other.message_id = '<temp@test>', '<temp@test>'
|
366
|
-
self.encoded == other.encoded
|
367
|
-
ensure
|
368
|
-
self.message_id, other.message_id = self_message_id, other_message_id
|
369
|
-
end
|
386
|
+
dup.tap { |m| m.message_id = '<temp@test>' }.encoded ==
|
387
|
+
other.dup.tap { |m| m.message_id = '<temp@test>' }.encoded
|
370
388
|
end
|
371
389
|
end
|
372
390
|
|
@@ -1193,8 +1211,8 @@ module Mail
|
|
1193
1211
|
def default( sym, val = nil )
|
1194
1212
|
if val
|
1195
1213
|
header[sym] = val
|
1196
|
-
|
1197
|
-
|
1214
|
+
elsif field = header[sym]
|
1215
|
+
field.default
|
1198
1216
|
end
|
1199
1217
|
end
|
1200
1218
|
|
@@ -1240,14 +1258,13 @@ module Mail
|
|
1240
1258
|
def body(value = nil)
|
1241
1259
|
if value
|
1242
1260
|
self.body = value
|
1243
|
-
# add_encoding_to_body
|
1244
1261
|
else
|
1245
1262
|
process_body_raw if @body_raw
|
1246
1263
|
@body
|
1247
1264
|
end
|
1248
1265
|
end
|
1249
1266
|
|
1250
|
-
def body_encoding(value)
|
1267
|
+
def body_encoding(value = nil)
|
1251
1268
|
if value.nil?
|
1252
1269
|
body.encoding
|
1253
1270
|
else
|
@@ -1256,7 +1273,7 @@ module Mail
|
|
1256
1273
|
end
|
1257
1274
|
|
1258
1275
|
def body_encoding=(value)
|
1259
|
-
|
1276
|
+
body.encoding = value
|
1260
1277
|
end
|
1261
1278
|
|
1262
1279
|
# Returns the list of addresses this message should be sent to by
|
@@ -1420,7 +1437,7 @@ module Mail
|
|
1420
1437
|
end
|
1421
1438
|
|
1422
1439
|
def has_transfer_encoding? # :nodoc:
|
1423
|
-
|
1440
|
+
warn(":has_transfer_encoding? is deprecated in Mail 1.4.3. Please use has_content_transfer_encoding?\n#{caller}")
|
1424
1441
|
has_content_transfer_encoding?
|
1425
1442
|
end
|
1426
1443
|
|
@@ -1468,34 +1485,26 @@ module Mail
|
|
1468
1485
|
if !body.empty?
|
1469
1486
|
# Only give a warning if this isn't an attachment, has non US-ASCII and the user
|
1470
1487
|
# has not specified an encoding explicitly.
|
1471
|
-
if @defaulted_charset && body.raw_source.
|
1488
|
+
if @defaulted_charset && !body.raw_source.ascii_only? && !self.attachment?
|
1472
1489
|
warning = "Non US-ASCII detected and no charset defined.\nDefaulting to UTF-8, set your own if this is incorrect.\n"
|
1473
|
-
|
1490
|
+
warn(warning)
|
1474
1491
|
end
|
1475
1492
|
header[:content_type].parameters['charset'] = @charset
|
1476
1493
|
end
|
1477
1494
|
end
|
1478
1495
|
|
1479
1496
|
# Adds a content transfer encoding
|
1480
|
-
#
|
1481
|
-
# Otherwise raises a warning
|
1482
1497
|
def add_content_transfer_encoding
|
1483
|
-
|
1484
|
-
header[:content_transfer_encoding] = '7bit'
|
1485
|
-
else
|
1486
|
-
warning = "Non US-ASCII detected and no content-transfer-encoding defined.\nDefaulting to 8bit, set your own if this is incorrect.\n"
|
1487
|
-
STDERR.puts(warning)
|
1488
|
-
header[:content_transfer_encoding] = '8bit'
|
1489
|
-
end
|
1498
|
+
header[:content_transfer_encoding] ||= body.default_encoding
|
1490
1499
|
end
|
1491
1500
|
|
1492
1501
|
def add_transfer_encoding # :nodoc:
|
1493
|
-
|
1502
|
+
warn(":add_transfer_encoding is deprecated in Mail 1.4.3. Please use add_content_transfer_encoding\n#{caller}")
|
1494
1503
|
add_content_transfer_encoding
|
1495
1504
|
end
|
1496
1505
|
|
1497
1506
|
def transfer_encoding # :nodoc:
|
1498
|
-
|
1507
|
+
warn(":transfer_encoding is deprecated in Mail 1.4.3. Please use content_transfer_encoding\n#{caller}")
|
1499
1508
|
content_transfer_encoding
|
1500
1509
|
end
|
1501
1510
|
|
@@ -1505,7 +1514,7 @@ module Mail
|
|
1505
1514
|
end
|
1506
1515
|
|
1507
1516
|
def message_content_type
|
1508
|
-
|
1517
|
+
warn(":message_content_type is deprecated in Mail 1.4.3. Please use mime_type\n#{caller}")
|
1509
1518
|
mime_type
|
1510
1519
|
end
|
1511
1520
|
|
@@ -1537,7 +1546,7 @@ module Mail
|
|
1537
1546
|
|
1538
1547
|
# Returns the content type parameters
|
1539
1548
|
def mime_parameters
|
1540
|
-
|
1549
|
+
warn(':mime_parameters is deprecated in Mail 1.4.3, please use :content_type_parameters instead')
|
1541
1550
|
content_type_parameters
|
1542
1551
|
end
|
1543
1552
|
|
@@ -1563,7 +1572,14 @@ module Mail
|
|
1563
1572
|
|
1564
1573
|
# returns the part in a multipart/report email that has the content-type delivery-status
|
1565
1574
|
def delivery_status_part
|
1566
|
-
|
1575
|
+
unless defined? @delivery_status_part
|
1576
|
+
@delivery_status_part =
|
1577
|
+
if delivery_status_report?
|
1578
|
+
parts.detect(&:delivery_status_report_part?)
|
1579
|
+
end
|
1580
|
+
end
|
1581
|
+
|
1582
|
+
@delivery_status_part
|
1567
1583
|
end
|
1568
1584
|
|
1569
1585
|
def bounced?
|
@@ -1778,9 +1794,6 @@ module Mail
|
|
1778
1794
|
else
|
1779
1795
|
basename = values[:filename]
|
1780
1796
|
filedata = values
|
1781
|
-
unless filedata[:content]
|
1782
|
-
filedata = values.merge(:content=>File.open(values[:filename], 'rb') { |f| f.read })
|
1783
|
-
end
|
1784
1797
|
end
|
1785
1798
|
self.attachments[basename] = filedata
|
1786
1799
|
end
|
@@ -1798,7 +1811,6 @@ module Mail
|
|
1798
1811
|
# ready to send
|
1799
1812
|
def ready_to_send!
|
1800
1813
|
identify_and_set_transfer_encoding
|
1801
|
-
parts.sort!([ "text/plain", "text/enriched", "text/html", "multipart/alternative" ])
|
1802
1814
|
parts.each do |part|
|
1803
1815
|
part.transport_encoding = transport_encoding
|
1804
1816
|
part.ready_to_send!
|
@@ -1807,7 +1819,7 @@ module Mail
|
|
1807
1819
|
end
|
1808
1820
|
|
1809
1821
|
def encode!
|
1810
|
-
|
1822
|
+
warn("Deprecated in 1.1.0 in favour of :ready_to_send! as it is less confusing with encoding and decoding.")
|
1811
1823
|
ready_to_send!
|
1812
1824
|
end
|
1813
1825
|
|
@@ -1823,16 +1835,13 @@ module Mail
|
|
1823
1835
|
end
|
1824
1836
|
|
1825
1837
|
def without_attachments!
|
1826
|
-
|
1827
|
-
|
1828
|
-
parts.delete_if { |p| p.attachment? }
|
1829
|
-
body_raw = if parts.empty?
|
1830
|
-
''
|
1831
|
-
else
|
1832
|
-
body.encoded
|
1833
|
-
end
|
1838
|
+
if has_attachments?
|
1839
|
+
parts.delete_if { |p| p.attachment? }
|
1834
1840
|
|
1835
|
-
|
1841
|
+
reencoded = parts.empty? ? '' : body.encoded(content_transfer_encoding)
|
1842
|
+
@body = nil # So the new parts won't be added to the existing body
|
1843
|
+
self.body = reencoded
|
1844
|
+
end
|
1836
1845
|
|
1837
1846
|
self
|
1838
1847
|
end
|
@@ -1975,7 +1984,7 @@ module Mail
|
|
1975
1984
|
|
1976
1985
|
private
|
1977
1986
|
|
1978
|
-
HEADER_SEPARATOR = /#{CRLF}#{CRLF}
|
1987
|
+
HEADER_SEPARATOR = /#{Constants::CRLF}#{Constants::CRLF}/
|
1979
1988
|
|
1980
1989
|
# 2.1. General Description
|
1981
1990
|
# A message consists of header fields (collectively called "the header
|
@@ -1984,9 +1993,6 @@ module Mail
|
|
1984
1993
|
# this standard. The body is simply a sequence of characters that
|
1985
1994
|
# follows the header and is separated from the header by an empty line
|
1986
1995
|
# (i.e., a line with nothing preceding the CRLF).
|
1987
|
-
#
|
1988
|
-
# Additionally, I allow for the case where someone might have put whitespace
|
1989
|
-
# on the "gap line"
|
1990
1996
|
def parse_message
|
1991
1997
|
header_part, body_part = raw_source.lstrip.split(HEADER_SEPARATOR, 2)
|
1992
1998
|
self.header = header_part
|
@@ -1994,8 +2000,7 @@ module Mail
|
|
1994
2000
|
end
|
1995
2001
|
|
1996
2002
|
def raw_source=(value)
|
1997
|
-
|
1998
|
-
@raw_source = ::Mail::Utilities.to_crlf(value)
|
2003
|
+
@raw_source = value
|
1999
2004
|
end
|
2000
2005
|
|
2001
2006
|
# see comments to body=. We take data and process it lazily
|
@@ -2007,11 +2012,9 @@ module Mail
|
|
2007
2012
|
@body_raw = nil
|
2008
2013
|
add_encoding_to_body
|
2009
2014
|
when @body && @body.multipart?
|
2010
|
-
|
2011
|
-
add_encoding_to_body
|
2015
|
+
self.text_part = value
|
2012
2016
|
else
|
2013
2017
|
@body_raw = value
|
2014
|
-
# process_body_raw
|
2015
2018
|
end
|
2016
2019
|
end
|
2017
2020
|
|
@@ -2026,7 +2029,7 @@ module Mail
|
|
2026
2029
|
|
2027
2030
|
def set_envelope_header
|
2028
2031
|
raw_string = raw_source.to_s
|
2029
|
-
if match_data =
|
2032
|
+
if match_data = raw_string.match(/\AFrom\s(#{TEXT}+)#{Constants::CRLF}/m)
|
2030
2033
|
set_envelope(match_data[1])
|
2031
2034
|
self.raw_source = raw_string.sub(match_data[0], "")
|
2032
2035
|
end
|
@@ -2036,6 +2039,13 @@ module Mail
|
|
2036
2039
|
body.split!(boundary)
|
2037
2040
|
end
|
2038
2041
|
|
2042
|
+
def allowed_encodings
|
2043
|
+
case mime_type
|
2044
|
+
when 'message/rfc822'
|
2045
|
+
[Encodings::SevenBit, Encodings::EightBit, Encodings::Binary]
|
2046
|
+
end
|
2047
|
+
end
|
2048
|
+
|
2039
2049
|
def add_encoding_to_body
|
2040
2050
|
if has_content_transfer_encoding?
|
2041
2051
|
@body.encoding = content_transfer_encoding
|
@@ -2043,11 +2053,11 @@ module Mail
|
|
2043
2053
|
end
|
2044
2054
|
|
2045
2055
|
def identify_and_set_transfer_encoding
|
2046
|
-
|
2047
|
-
|
2048
|
-
|
2049
|
-
|
2050
|
-
|
2056
|
+
if body && body.multipart?
|
2057
|
+
self.content_transfer_encoding = @transport_encoding
|
2058
|
+
else
|
2059
|
+
self.content_transfer_encoding = body.negotiate_best_encoding(@transport_encoding, allowed_encodings).to_s
|
2060
|
+
end
|
2051
2061
|
end
|
2052
2062
|
|
2053
2063
|
def add_required_fields
|
@@ -2130,10 +2140,10 @@ module Mail
|
|
2130
2140
|
content_disp_name = header[:content_disposition].filename rescue nil
|
2131
2141
|
content_loc_name = header[:content_location].location rescue nil
|
2132
2142
|
case
|
2133
|
-
when content_type && content_type_name
|
2134
|
-
filename = content_type_name
|
2135
2143
|
when content_disposition && content_disp_name
|
2136
2144
|
filename = content_disp_name
|
2145
|
+
when content_type && content_type_name
|
2146
|
+
filename = content_type_name
|
2137
2147
|
when content_location && content_loc_name
|
2138
2148
|
filename = content_loc_name
|
2139
2149
|
else
|
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:
|
@@ -40,7 +41,7 @@ module Mail #:nodoc:
|
|
40
41
|
if RUBY_VERSION >= "1.9"
|
41
42
|
# Creates a new Chars instance by wrapping _string_.
|
42
43
|
def initialize(string)
|
43
|
-
@wrapped_string = string
|
44
|
+
@wrapped_string = string.dup
|
44
45
|
@wrapped_string.force_encoding(Encoding::UTF_8) unless @wrapped_string.frozen?
|
45
46
|
end
|
46
47
|
else
|
@@ -103,7 +104,7 @@ module Mail #:nodoc:
|
|
103
104
|
# Returns a new Chars object containing the _other_ object concatenated to the string.
|
104
105
|
#
|
105
106
|
# Example:
|
106
|
-
# ('Café'
|
107
|
+
# (Mail::Multibyte.mb_chars('Café') + ' périferôl').to_s # => "Café périferôl"
|
107
108
|
def +(other)
|
108
109
|
chars(@wrapped_string + other)
|
109
110
|
end
|
@@ -111,7 +112,7 @@ module Mail #:nodoc:
|
|
111
112
|
# Like <tt>String#=~</tt> only it returns the character offset (in codepoints) instead of the byte offset.
|
112
113
|
#
|
113
114
|
# Example:
|
114
|
-
# 'Café périferôl'
|
115
|
+
# Mail::Multibyte.mb_chars('Café périferôl') =~ /ô/ # => 12
|
115
116
|
def =~(other)
|
116
117
|
translate_offset(@wrapped_string =~ other)
|
117
118
|
end
|
@@ -119,7 +120,7 @@ module Mail #:nodoc:
|
|
119
120
|
# Inserts the passed string at specified codepoint offsets.
|
120
121
|
#
|
121
122
|
# Example:
|
122
|
-
# 'Café'.
|
123
|
+
# Mail::Multibyte.mb_chars('Café').insert(4, ' périferôl').to_s # => "Café périferôl"
|
123
124
|
def insert(offset, fragment)
|
124
125
|
unpacked = Unicode.u_unpack(@wrapped_string)
|
125
126
|
unless offset > unpacked.length
|
@@ -135,7 +136,7 @@ module Mail #:nodoc:
|
|
135
136
|
# Returns +true+ if contained string contains _other_. Returns +false+ otherwise.
|
136
137
|
#
|
137
138
|
# Example:
|
138
|
-
# 'Café'.
|
139
|
+
# Mail::Multibyte.mb_chars('Café').include?('é') # => true
|
139
140
|
def include?(other)
|
140
141
|
# We have to redefine this method because Enumerable defines it.
|
141
142
|
@wrapped_string.include?(other)
|
@@ -144,8 +145,8 @@ module Mail #:nodoc:
|
|
144
145
|
# Returns the position _needle_ in the string, counting in codepoints. Returns +nil+ if _needle_ isn't found.
|
145
146
|
#
|
146
147
|
# Example:
|
147
|
-
# 'Café périferôl'.
|
148
|
-
# '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
|
149
150
|
def index(needle, offset=0)
|
150
151
|
wrapped_offset = first(offset).wrapped_string.length
|
151
152
|
index = @wrapped_string.index(needle, wrapped_offset)
|
@@ -157,8 +158,8 @@ module Mail #:nodoc:
|
|
157
158
|
# string. Returns +nil+ if _needle_ isn't found.
|
158
159
|
#
|
159
160
|
# Example:
|
160
|
-
# 'Café périferôl'.
|
161
|
-
# '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
|
162
163
|
def rindex(needle, offset=nil)
|
163
164
|
offset ||= length
|
164
165
|
wrapped_offset = first(offset).wrapped_string.length
|
@@ -190,7 +191,7 @@ module Mail #:nodoc:
|
|
190
191
|
# Returns the codepoint of the first character in the string.
|
191
192
|
#
|
192
193
|
# Example:
|
193
|
-
# 'こんにちは'.
|
194
|
+
# Mail::Multibyte.mb_chars('こんにちは').ord # => 12371
|
194
195
|
def ord
|
195
196
|
Unicode.u_unpack(@wrapped_string)[0]
|
196
197
|
end
|
@@ -199,10 +200,10 @@ module Mail #:nodoc:
|
|
199
200
|
#
|
200
201
|
# Example:
|
201
202
|
#
|
202
|
-
# "¾ cup".
|
203
|
+
# Mail::Multibyte.mb_chars("¾ cup").rjust(8).to_s
|
203
204
|
# # => " ¾ cup"
|
204
205
|
#
|
205
|
-
# "¾ cup".
|
206
|
+
# Mail::Multibyte.mb_chars("¾ cup").rjust(8, " ").to_s # Use non-breaking whitespace
|
206
207
|
# # => " ¾ cup"
|
207
208
|
def rjust(integer, padstr=' ')
|
208
209
|
justify(integer, :right, padstr)
|
@@ -212,10 +213,10 @@ module Mail #:nodoc:
|
|
212
213
|
#
|
213
214
|
# Example:
|
214
215
|
#
|
215
|
-
# "¾ cup".
|
216
|
+
# Mail::Multibyte.mb_chars("¾ cup").rjust(8).to_s
|
216
217
|
# # => "¾ cup "
|
217
218
|
#
|
218
|
-
# "¾ cup".
|
219
|
+
# Mail::Multibyte.mb_chars("¾ cup").rjust(8, " ").to_s # Use non-breaking whitespace
|
219
220
|
# # => "¾ cup "
|
220
221
|
def ljust(integer, padstr=' ')
|
221
222
|
justify(integer, :left, padstr)
|
@@ -225,10 +226,10 @@ module Mail #:nodoc:
|
|
225
226
|
#
|
226
227
|
# Example:
|
227
228
|
#
|
228
|
-
# "¾ cup".
|
229
|
+
# Mail::Multibyte.mb_chars("¾ cup").center(8).to_s
|
229
230
|
# # => " ¾ cup "
|
230
231
|
#
|
231
|
-
# "¾ cup".
|
232
|
+
# Mail::Multibyte.mb_chars("¾ cup").center(8, " ").to_s # Use non-breaking whitespace
|
232
233
|
# # => " ¾ cup "
|
233
234
|
def center(integer, padstr=' ')
|
234
235
|
justify(integer, :center, padstr)
|
@@ -244,7 +245,7 @@ module Mail #:nodoc:
|
|
244
245
|
# instances instead of String. This makes chaining methods easier.
|
245
246
|
#
|
246
247
|
# Example:
|
247
|
-
# 'Café périferôl'.
|
248
|
+
# Mail::Multibyte.mb_chars('Café périferôl').split(/é/).map { |part| part.upcase.to_s } # => ["CAF", " P", "RIFERÔL"]
|
248
249
|
def split(*args)
|
249
250
|
@wrapped_string.split(*args).map { |i| i.mb_chars }
|
250
251
|
end
|
@@ -292,7 +293,7 @@ module Mail #:nodoc:
|
|
292
293
|
# Reverses all characters in the string.
|
293
294
|
#
|
294
295
|
# Example:
|
295
|
-
# 'Café'.
|
296
|
+
# Mail::Multibyte.mb_chars('Café').reverse.to_s # => 'éfaC'
|
296
297
|
def reverse
|
297
298
|
chars(Unicode.g_unpack(@wrapped_string).reverse.flatten.pack('U*'))
|
298
299
|
end
|
@@ -301,7 +302,7 @@ module Mail #:nodoc:
|
|
301
302
|
# character.
|
302
303
|
#
|
303
304
|
# Example:
|
304
|
-
# 'こんにちは'.
|
305
|
+
# Mail::Multibyte.mb_chars('こんにちは').slice(2..3).to_s # => "にち"
|
305
306
|
def slice(*args)
|
306
307
|
if args.size > 2
|
307
308
|
raise ArgumentError, "wrong number of arguments (#{args.size} for 1)" # Do as if we were native
|
@@ -338,7 +339,7 @@ module Mail #:nodoc:
|
|
338
339
|
# Convert characters in the string to uppercase.
|
339
340
|
#
|
340
341
|
# Example:
|
341
|
-
# 'Laurent, où sont les tests ?'.
|
342
|
+
# Mail::Multibyte.mb_chars('Laurent, où sont les tests ?').upcase.to_s # => "LAURENT, OÙ SONT LES TESTS ?"
|
342
343
|
def upcase
|
343
344
|
chars(Unicode.apply_mapping(@wrapped_string, :uppercase_mapping))
|
344
345
|
end
|
@@ -346,7 +347,7 @@ module Mail #:nodoc:
|
|
346
347
|
# Convert characters in the string to lowercase.
|
347
348
|
#
|
348
349
|
# Example:
|
349
|
-
# 'VĚDA A VÝZKUM'.
|
350
|
+
# Mail::Multibyte.mb_chars('VĚDA A VÝZKUM').downcase.to_s # => "věda a výzkum"
|
350
351
|
def downcase
|
351
352
|
chars(Unicode.apply_mapping(@wrapped_string, :lowercase_mapping))
|
352
353
|
end
|
@@ -354,7 +355,7 @@ module Mail #:nodoc:
|
|
354
355
|
# Converts the first character to uppercase and the remainder to lowercase.
|
355
356
|
#
|
356
357
|
# Example:
|
357
|
-
# 'über'.
|
358
|
+
# Mail::Multibyte.mb_chars('über').capitalize.to_s # => "Über"
|
358
359
|
def capitalize
|
359
360
|
(slice(0) || chars('')).upcase + (slice(1..-1) || chars('')).downcase
|
360
361
|
end
|
@@ -362,8 +363,8 @@ module Mail #:nodoc:
|
|
362
363
|
# Capitalizes the first letter of every word, when possible.
|
363
364
|
#
|
364
365
|
# Example:
|
365
|
-
# "ÉL QUE SE ENTERÓ".
|
366
|
-
# "日本語".
|
366
|
+
# Mail::Multibyte.mb_chars("ÉL QUE SE ENTERÓ").titleize # => "Él Que Se Enteró"
|
367
|
+
# Mail::Multibyte.mb_chars("日本語").titleize # => "日本語"
|
367
368
|
def titleize
|
368
369
|
chars(downcase.to_s.gsub(/\b('?\S)/u) { Unicode.apply_mapping $1, :uppercase_mapping })
|
369
370
|
end
|
@@ -383,7 +384,7 @@ module Mail #:nodoc:
|
|
383
384
|
#
|
384
385
|
# Example:
|
385
386
|
# 'é'.length # => 2
|
386
|
-
# 'é'.
|
387
|
+
# Mail::Multibyte.mb_chars('é').decompose.to_s.length # => 3
|
387
388
|
def decompose
|
388
389
|
chars(Unicode.decompose_codepoints(:canonical, Unicode.u_unpack(@wrapped_string)).pack('U*'))
|
389
390
|
end
|
@@ -392,7 +393,7 @@ module Mail #:nodoc:
|
|
392
393
|
#
|
393
394
|
# Example:
|
394
395
|
# 'é'.length # => 3
|
395
|
-
# 'é'.
|
396
|
+
# Mail::Multibyte.mb_chars('é').compose.to_s.length # => 2
|
396
397
|
def compose
|
397
398
|
chars(Unicode.compose_codepoints(Unicode.u_unpack(@wrapped_string)).pack('U*'))
|
398
399
|
end
|
@@ -400,8 +401,8 @@ module Mail #:nodoc:
|
|
400
401
|
# Returns the number of grapheme clusters in the string.
|
401
402
|
#
|
402
403
|
# Example:
|
403
|
-
# 'क्षि'.
|
404
|
-
# 'क्षि'.
|
404
|
+
# Mail::Multibyte.mb_chars('क्षि').length # => 4
|
405
|
+
# Mail::Multibyte.mb_chars('क्षि').g_length # => 3
|
405
406
|
def g_length
|
406
407
|
Unicode.g_unpack(@wrapped_string).length
|
407
408
|
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.rb
CHANGED
@@ -1,24 +1,73 @@
|
|
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
|
-
|
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
|
22
71
|
end
|
23
72
|
|
24
73
|
# Regular expressions that describe valid byte sequences for a character
|
@@ -40,4 +89,4 @@ module Mail #:nodoc:
|
|
40
89
|
end
|
41
90
|
end
|
42
91
|
|
43
|
-
require 'mail/multibyte/utils'
|
92
|
+
require 'mail/multibyte/utils'
|