mail 2.6.3 → 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 +91 -79
- data/lib/mail/attachments_list.rb +11 -5
- data/lib/mail/body.rb +54 -41
- data/lib/mail/check_delivery_params.rb +50 -10
- data/lib/mail/configuration.rb +3 -0
- data/lib/mail/constants.rb +5 -3
- data/lib/mail/core_extensions/smtp.rb +20 -16
- data/lib/mail/core_extensions/string.rb +1 -30
- data/lib/mail/elements/address.rb +43 -32
- data/lib/mail/elements/address_list.rb +11 -18
- data/lib/mail/elements/content_disposition_element.rb +9 -15
- data/lib/mail/elements/content_location_element.rb +8 -12
- data/lib/mail/elements/content_transfer_encoding_element.rb +6 -10
- data/lib/mail/elements/content_type_element.rb +9 -19
- data/lib/mail/elements/date_time_element.rb +7 -14
- data/lib/mail/elements/envelope_from_element.rb +15 -21
- data/lib/mail/elements/message_ids_element.rb +12 -14
- data/lib/mail/elements/mime_version_element.rb +7 -14
- data/lib/mail/elements/phrase_list.rb +7 -9
- data/lib/mail/elements/received_element.rb +10 -15
- data/lib/mail/elements.rb +1 -0
- data/lib/mail/encodings/7bit.rb +6 -15
- data/lib/mail/encodings/8bit.rb +5 -18
- data/lib/mail/encodings/base64.rb +15 -10
- data/lib/mail/encodings/binary.rb +4 -22
- data/lib/mail/encodings/identity.rb +24 -0
- data/lib/mail/encodings/quoted_printable.rb +13 -7
- data/lib/mail/encodings/transfer_encoding.rb +47 -28
- data/lib/mail/encodings/unix_to_unix.rb +4 -1
- data/lib/mail/encodings.rb +114 -60
- data/lib/mail/envelope.rb +2 -1
- data/lib/mail/field.rb +114 -62
- data/lib/mail/field_list.rb +1 -0
- data/lib/mail/fields/bcc_field.rb +17 -5
- data/lib/mail/fields/cc_field.rb +2 -2
- data/lib/mail/fields/comments_field.rb +2 -1
- data/lib/mail/fields/common/address_container.rb +3 -2
- data/lib/mail/fields/common/common_address.rb +40 -14
- data/lib/mail/fields/common/common_date.rb +2 -1
- data/lib/mail/fields/common/common_field.rb +5 -11
- data/lib/mail/fields/common/common_message_id.rb +3 -2
- data/lib/mail/fields/common/parameter_hash.rb +2 -1
- data/lib/mail/fields/content_description_field.rb +2 -1
- data/lib/mail/fields/content_disposition_field.rb +14 -13
- data/lib/mail/fields/content_id_field.rb +5 -4
- data/lib/mail/fields/content_location_field.rb +3 -2
- data/lib/mail/fields/content_transfer_encoding_field.rb +3 -2
- data/lib/mail/fields/content_type_field.rb +7 -11
- data/lib/mail/fields/date_field.rb +4 -4
- data/lib/mail/fields/from_field.rb +2 -2
- data/lib/mail/fields/in_reply_to_field.rb +2 -1
- data/lib/mail/fields/keywords_field.rb +3 -3
- data/lib/mail/fields/message_id_field.rb +3 -2
- data/lib/mail/fields/mime_version_field.rb +4 -3
- data/lib/mail/fields/optional_field.rb +5 -1
- data/lib/mail/fields/received_field.rb +5 -4
- data/lib/mail/fields/references_field.rb +2 -1
- data/lib/mail/fields/reply_to_field.rb +2 -2
- data/lib/mail/fields/resent_bcc_field.rb +2 -2
- data/lib/mail/fields/resent_cc_field.rb +2 -2
- data/lib/mail/fields/resent_date_field.rb +2 -2
- data/lib/mail/fields/resent_from_field.rb +2 -2
- data/lib/mail/fields/resent_message_id_field.rb +2 -1
- data/lib/mail/fields/resent_sender_field.rb +2 -2
- data/lib/mail/fields/resent_to_field.rb +2 -2
- data/lib/mail/fields/return_path_field.rb +2 -2
- data/lib/mail/fields/sender_field.rb +2 -2
- data/lib/mail/fields/structured_field.rb +1 -0
- data/lib/mail/fields/subject_field.rb +2 -1
- data/lib/mail/fields/to_field.rb +2 -2
- data/lib/mail/fields/unstructured_field.rb +25 -7
- data/lib/mail/fields.rb +1 -0
- data/lib/mail/header.rb +15 -12
- data/lib/mail/indifferent_hash.rb +1 -0
- data/lib/mail/mail.rb +3 -10
- data/lib/mail/matchers/attachment_matchers.rb +29 -0
- data/lib/mail/matchers/has_sent_mail.rb +51 -7
- data/lib/mail/message.rb +91 -86
- data/lib/mail/multibyte/chars.rb +32 -30
- data/lib/mail/multibyte/unicode.rb +31 -26
- data/lib/mail/multibyte/utils.rb +1 -0
- data/lib/mail/multibyte.rb +65 -15
- data/lib/mail/network/delivery_methods/exim.rb +7 -10
- data/lib/mail/network/delivery_methods/file_delivery.rb +5 -8
- data/lib/mail/network/delivery_methods/logger_delivery.rb +37 -0
- data/lib/mail/network/delivery_methods/sendmail.rb +17 -11
- data/lib/mail/network/delivery_methods/smtp.rb +60 -53
- data/lib/mail/network/delivery_methods/smtp_connection.rb +11 -6
- data/lib/mail/network/delivery_methods/test_mailer.rb +6 -8
- data/lib/mail/network/retriever_methods/base.rb +1 -0
- data/lib/mail/network/retriever_methods/imap.rb +19 -5
- data/lib/mail/network/retriever_methods/pop3.rb +4 -1
- data/lib/mail/network/retriever_methods/test_retriever.rb +2 -1
- data/lib/mail/network.rb +2 -0
- data/lib/mail/parser_tools.rb +15 -0
- data/lib/mail/parsers/address_lists_parser.rb +33208 -104
- data/lib/mail/parsers/address_lists_parser.rl +172 -0
- data/lib/mail/parsers/content_disposition_parser.rb +877 -49
- data/lib/mail/parsers/content_disposition_parser.rl +82 -0
- data/lib/mail/parsers/content_location_parser.rb +804 -23
- data/lib/mail/parsers/content_location_parser.rl +71 -0
- data/lib/mail/parsers/content_transfer_encoding_parser.rb +502 -19
- data/lib/mail/parsers/content_transfer_encoding_parser.rl +64 -0
- data/lib/mail/parsers/content_type_parser.rb +1024 -48
- data/lib/mail/parsers/content_type_parser.rl +83 -0
- data/lib/mail/parsers/date_time_parser.rb +872 -23
- data/lib/mail/parsers/date_time_parser.rl +62 -0
- data/lib/mail/parsers/envelope_from_parser.rb +3570 -34
- data/lib/mail/parsers/envelope_from_parser.rl +82 -0
- data/lib/mail/parsers/message_ids_parser.rb +2840 -25
- data/lib/mail/parsers/message_ids_parser.rl +82 -0
- data/lib/mail/parsers/mime_version_parser.rb +492 -26
- data/lib/mail/parsers/mime_version_parser.rl +61 -0
- data/lib/mail/parsers/phrase_lists_parser.rb +862 -17
- data/lib/mail/parsers/phrase_lists_parser.rl +83 -0
- data/lib/mail/parsers/received_parser.rb +8765 -36
- data/lib/mail/parsers/received_parser.rl +84 -0
- data/lib/mail/parsers/rfc2045_content_transfer_encoding.rl +13 -0
- data/lib/mail/parsers/rfc2045_content_type.rl +25 -0
- data/lib/mail/parsers/rfc2045_mime.rl +16 -0
- data/lib/mail/parsers/rfc2183_content_disposition.rl +15 -0
- data/lib/mail/parsers/rfc3629_utf8.rl +19 -0
- data/lib/mail/parsers/rfc5234_abnf_core_rules.rl +22 -0
- data/lib/mail/parsers/rfc5322.rl +59 -0
- data/lib/mail/parsers/rfc5322_address.rl +72 -0
- data/lib/mail/parsers/{ragel/date_time.rl → rfc5322_date_time.rl} +8 -1
- data/lib/mail/parsers/rfc5322_lexical_tokens.rl +60 -0
- data/lib/mail/parsers.rb +17 -24
- data/lib/mail/part.rb +8 -5
- data/lib/mail/parts_list.rb +31 -14
- data/lib/mail/utilities.rb +109 -10
- data/lib/mail/values/unicode_tables.dat +0 -0
- data/lib/mail/version.rb +3 -2
- data/lib/mail/version_specific/ruby_1_8.rb +50 -6
- data/lib/mail/version_specific/ruby_1_9.rb +103 -18
- data/lib/mail.rb +5 -12
- metadata +47 -57
- data/CHANGELOG.rdoc +0 -759
- data/CONTRIBUTING.md +0 -60
- data/Dependencies.txt +0 -2
- data/Gemfile +0 -15
- data/Rakefile +0 -29
- data/TODO.rdoc +0 -9
- data/lib/mail/core_extensions/nil.rb +0 -19
- data/lib/mail/core_extensions/object.rb +0 -13
- data/lib/mail/core_extensions/string/access.rb +0 -145
- data/lib/mail/core_extensions/string/multibyte.rb +0 -78
- data/lib/mail/multibyte/exceptions.rb +0 -8
- data/lib/mail/parsers/ragel/common.rl +0 -185
- data/lib/mail/parsers/ragel/parser_info.rb +0 -61
- data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +0 -14864
- data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +0 -751
- data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +0 -614
- data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +0 -447
- data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +0 -825
- data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +0 -817
- data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +0 -2149
- data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +0 -1570
- data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +0 -440
- data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +0 -564
- data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +0 -51
- data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +0 -5144
- data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +0 -37
- data/lib/mail/parsers/ragel/ruby.rb +0 -40
- data/lib/mail/parsers/ragel.rb +0 -17
data/lib/mail/message.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
require "yaml"
|
|
3
4
|
|
|
4
5
|
module Mail
|
|
@@ -55,15 +56,23 @@ module Mail
|
|
|
55
56
|
#
|
|
56
57
|
# ===Making an email via a block
|
|
57
58
|
#
|
|
58
|
-
# mail = Mail.new do
|
|
59
|
-
#
|
|
60
|
-
#
|
|
61
|
-
# subject 'This is a test email'
|
|
62
|
-
#
|
|
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')
|
|
63
64
|
# end
|
|
64
65
|
#
|
|
65
66
|
# mail.to_s #=> "From: mikel@test.lindsaar.net\r\nTo: you@...
|
|
66
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
|
+
#
|
|
67
76
|
# ===Making an email via passing a string
|
|
68
77
|
#
|
|
69
78
|
# mail = Mail.new("To: mikel@test.lindsaar.net\r\nSubject: Hello\r\n\r\nHi there!")
|
|
@@ -128,8 +137,23 @@ module Mail
|
|
|
128
137
|
init_with_string(args.flatten[0].to_s)
|
|
129
138
|
end
|
|
130
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
|
|
131
151
|
if block_given?
|
|
132
|
-
|
|
152
|
+
if block.arity.zero? || (RUBY_VERSION < '1.9' && block.arity < 1)
|
|
153
|
+
instance_eval(&block)
|
|
154
|
+
else
|
|
155
|
+
yield self
|
|
156
|
+
end
|
|
133
157
|
end
|
|
134
158
|
|
|
135
159
|
self
|
|
@@ -212,7 +236,7 @@ module Mail
|
|
|
212
236
|
self.default_charset = 'UTF-8'
|
|
213
237
|
|
|
214
238
|
def register_for_delivery_notification(observer)
|
|
215
|
-
|
|
239
|
+
warn("Message#register_for_delivery_notification is deprecated, please call Mail.register_observer instead")
|
|
216
240
|
Mail.register_observer(observer)
|
|
217
241
|
end
|
|
218
242
|
|
|
@@ -224,7 +248,7 @@ module Mail
|
|
|
224
248
|
Mail.inform_interceptors(self)
|
|
225
249
|
end
|
|
226
250
|
|
|
227
|
-
# Delivers
|
|
251
|
+
# Delivers a mail object.
|
|
228
252
|
#
|
|
229
253
|
# Examples:
|
|
230
254
|
#
|
|
@@ -359,13 +383,8 @@ module Mail
|
|
|
359
383
|
if self.message_id && other.message_id
|
|
360
384
|
self.encoded == other.encoded
|
|
361
385
|
else
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
self.message_id, other.message_id = '<temp@test>', '<temp@test>'
|
|
365
|
-
self.encoded == other.encoded
|
|
366
|
-
ensure
|
|
367
|
-
self.message_id, other.message_id = self_message_id, other_message_id
|
|
368
|
-
end
|
|
386
|
+
dup.tap { |m| m.message_id = '<temp@test>' }.encoded ==
|
|
387
|
+
other.dup.tap { |m| m.message_id = '<temp@test>' }.encoded
|
|
369
388
|
end
|
|
370
389
|
end
|
|
371
390
|
|
|
@@ -1192,8 +1211,8 @@ module Mail
|
|
|
1192
1211
|
def default( sym, val = nil )
|
|
1193
1212
|
if val
|
|
1194
1213
|
header[sym] = val
|
|
1195
|
-
|
|
1196
|
-
|
|
1214
|
+
elsif field = header[sym]
|
|
1215
|
+
field.default
|
|
1197
1216
|
end
|
|
1198
1217
|
end
|
|
1199
1218
|
|
|
@@ -1239,14 +1258,13 @@ module Mail
|
|
|
1239
1258
|
def body(value = nil)
|
|
1240
1259
|
if value
|
|
1241
1260
|
self.body = value
|
|
1242
|
-
# add_encoding_to_body
|
|
1243
1261
|
else
|
|
1244
1262
|
process_body_raw if @body_raw
|
|
1245
1263
|
@body
|
|
1246
1264
|
end
|
|
1247
1265
|
end
|
|
1248
1266
|
|
|
1249
|
-
def body_encoding(value)
|
|
1267
|
+
def body_encoding(value = nil)
|
|
1250
1268
|
if value.nil?
|
|
1251
1269
|
body.encoding
|
|
1252
1270
|
else
|
|
@@ -1255,7 +1273,7 @@ module Mail
|
|
|
1255
1273
|
end
|
|
1256
1274
|
|
|
1257
1275
|
def body_encoding=(value)
|
|
1258
|
-
|
|
1276
|
+
body.encoding = value
|
|
1259
1277
|
end
|
|
1260
1278
|
|
|
1261
1279
|
# Returns the list of addresses this message should be sent to by
|
|
@@ -1415,11 +1433,11 @@ module Mail
|
|
|
1415
1433
|
end
|
|
1416
1434
|
|
|
1417
1435
|
def has_content_transfer_encoding?
|
|
1418
|
-
header[:content_transfer_encoding] && header[:content_transfer_encoding].errors
|
|
1436
|
+
header[:content_transfer_encoding] && Utilities.blank?(header[:content_transfer_encoding].errors)
|
|
1419
1437
|
end
|
|
1420
1438
|
|
|
1421
1439
|
def has_transfer_encoding? # :nodoc:
|
|
1422
|
-
|
|
1440
|
+
warn(":has_transfer_encoding? is deprecated in Mail 1.4.3. Please use has_content_transfer_encoding?\n#{caller}")
|
|
1423
1441
|
has_content_transfer_encoding?
|
|
1424
1442
|
end
|
|
1425
1443
|
|
|
@@ -1467,34 +1485,26 @@ module Mail
|
|
|
1467
1485
|
if !body.empty?
|
|
1468
1486
|
# Only give a warning if this isn't an attachment, has non US-ASCII and the user
|
|
1469
1487
|
# has not specified an encoding explicitly.
|
|
1470
|
-
if @defaulted_charset && body.raw_source.
|
|
1488
|
+
if @defaulted_charset && !body.raw_source.ascii_only? && !self.attachment?
|
|
1471
1489
|
warning = "Non US-ASCII detected and no charset defined.\nDefaulting to UTF-8, set your own if this is incorrect.\n"
|
|
1472
|
-
|
|
1490
|
+
warn(warning)
|
|
1473
1491
|
end
|
|
1474
1492
|
header[:content_type].parameters['charset'] = @charset
|
|
1475
1493
|
end
|
|
1476
1494
|
end
|
|
1477
1495
|
|
|
1478
1496
|
# Adds a content transfer encoding
|
|
1479
|
-
#
|
|
1480
|
-
# Otherwise raises a warning
|
|
1481
1497
|
def add_content_transfer_encoding
|
|
1482
|
-
|
|
1483
|
-
header[:content_transfer_encoding] = '7bit'
|
|
1484
|
-
else
|
|
1485
|
-
warning = "Non US-ASCII detected and no content-transfer-encoding defined.\nDefaulting to 8bit, set your own if this is incorrect.\n"
|
|
1486
|
-
STDERR.puts(warning)
|
|
1487
|
-
header[:content_transfer_encoding] = '8bit'
|
|
1488
|
-
end
|
|
1498
|
+
header[:content_transfer_encoding] ||= body.default_encoding
|
|
1489
1499
|
end
|
|
1490
1500
|
|
|
1491
1501
|
def add_transfer_encoding # :nodoc:
|
|
1492
|
-
|
|
1502
|
+
warn(":add_transfer_encoding is deprecated in Mail 1.4.3. Please use add_content_transfer_encoding\n#{caller}")
|
|
1493
1503
|
add_content_transfer_encoding
|
|
1494
1504
|
end
|
|
1495
1505
|
|
|
1496
1506
|
def transfer_encoding # :nodoc:
|
|
1497
|
-
|
|
1507
|
+
warn(":transfer_encoding is deprecated in Mail 1.4.3. Please use content_transfer_encoding\n#{caller}")
|
|
1498
1508
|
content_transfer_encoding
|
|
1499
1509
|
end
|
|
1500
1510
|
|
|
@@ -1504,7 +1514,7 @@ module Mail
|
|
|
1504
1514
|
end
|
|
1505
1515
|
|
|
1506
1516
|
def message_content_type
|
|
1507
|
-
|
|
1517
|
+
warn(":message_content_type is deprecated in Mail 1.4.3. Please use mime_type\n#{caller}")
|
|
1508
1518
|
mime_type
|
|
1509
1519
|
end
|
|
1510
1520
|
|
|
@@ -1536,7 +1546,7 @@ module Mail
|
|
|
1536
1546
|
|
|
1537
1547
|
# Returns the content type parameters
|
|
1538
1548
|
def mime_parameters
|
|
1539
|
-
|
|
1549
|
+
warn(':mime_parameters is deprecated in Mail 1.4.3, please use :content_type_parameters instead')
|
|
1540
1550
|
content_type_parameters
|
|
1541
1551
|
end
|
|
1542
1552
|
|
|
@@ -1562,7 +1572,14 @@ module Mail
|
|
|
1562
1572
|
|
|
1563
1573
|
# returns the part in a multipart/report email that has the content-type delivery-status
|
|
1564
1574
|
def delivery_status_part
|
|
1565
|
-
|
|
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
|
|
1566
1583
|
end
|
|
1567
1584
|
|
|
1568
1585
|
def bounced?
|
|
@@ -1669,6 +1686,8 @@ module Mail
|
|
|
1669
1686
|
def html_part=(msg)
|
|
1670
1687
|
# Assign the html part and set multipart/alternative if there's a text part.
|
|
1671
1688
|
if msg
|
|
1689
|
+
msg = Mail::Part.new(:body => msg) unless msg.kind_of?(Mail::Message)
|
|
1690
|
+
|
|
1672
1691
|
@html_part = msg
|
|
1673
1692
|
@html_part.content_type = 'text/html' unless @html_part.has_content_type?
|
|
1674
1693
|
add_multipart_alternate_header if text_part
|
|
@@ -1691,6 +1710,8 @@ module Mail
|
|
|
1691
1710
|
def text_part=(msg)
|
|
1692
1711
|
# Assign the text part and set multipart/alternative if there's an html part.
|
|
1693
1712
|
if msg
|
|
1713
|
+
msg = Mail::Part.new(:body => msg) unless msg.kind_of?(Mail::Message)
|
|
1714
|
+
|
|
1694
1715
|
@text_part = msg
|
|
1695
1716
|
@text_part.content_type = 'text/plain' unless @text_part.has_content_type?
|
|
1696
1717
|
add_multipart_alternate_header if html_part
|
|
@@ -1709,7 +1730,7 @@ module Mail
|
|
|
1709
1730
|
|
|
1710
1731
|
# Adds a part to the parts list or creates the part list
|
|
1711
1732
|
def add_part(part)
|
|
1712
|
-
if !body.multipart? && !self.body.decoded
|
|
1733
|
+
if !body.multipart? && !Utilities.blank?(self.body.decoded)
|
|
1713
1734
|
@text_part = Mail::Part.new('Content-Type: text/plain;')
|
|
1714
1735
|
@text_part.body = body.decoded
|
|
1715
1736
|
self.body << @text_part
|
|
@@ -1765,14 +1786,14 @@ module Mail
|
|
|
1765
1786
|
#
|
|
1766
1787
|
# See also #attachments
|
|
1767
1788
|
def add_file(values)
|
|
1768
|
-
convert_to_multipart unless self.multipart? || self.body.decoded
|
|
1789
|
+
convert_to_multipart unless self.multipart? || Utilities.blank?(self.body.decoded)
|
|
1769
1790
|
add_multipart_mixed_header
|
|
1770
1791
|
if values.is_a?(String)
|
|
1771
1792
|
basename = File.basename(values)
|
|
1772
1793
|
filedata = File.open(values, 'rb') { |f| f.read }
|
|
1773
1794
|
else
|
|
1774
1795
|
basename = values[:filename]
|
|
1775
|
-
filedata = values
|
|
1796
|
+
filedata = values
|
|
1776
1797
|
end
|
|
1777
1798
|
self.attachments[basename] = filedata
|
|
1778
1799
|
end
|
|
@@ -1790,7 +1811,6 @@ module Mail
|
|
|
1790
1811
|
# ready to send
|
|
1791
1812
|
def ready_to_send!
|
|
1792
1813
|
identify_and_set_transfer_encoding
|
|
1793
|
-
parts.sort!([ "text/plain", "text/enriched", "text/html", "multipart/alternative" ])
|
|
1794
1814
|
parts.each do |part|
|
|
1795
1815
|
part.transport_encoding = transport_encoding
|
|
1796
1816
|
part.ready_to_send!
|
|
@@ -1799,7 +1819,7 @@ module Mail
|
|
|
1799
1819
|
end
|
|
1800
1820
|
|
|
1801
1821
|
def encode!
|
|
1802
|
-
|
|
1822
|
+
warn("Deprecated in 1.1.0 in favour of :ready_to_send! as it is less confusing with encoding and decoding.")
|
|
1803
1823
|
ready_to_send!
|
|
1804
1824
|
end
|
|
1805
1825
|
|
|
@@ -1815,16 +1835,13 @@ module Mail
|
|
|
1815
1835
|
end
|
|
1816
1836
|
|
|
1817
1837
|
def without_attachments!
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
parts.delete_if { |p| p.attachment? }
|
|
1821
|
-
body_raw = if parts.empty?
|
|
1822
|
-
''
|
|
1823
|
-
else
|
|
1824
|
-
body.encoded
|
|
1825
|
-
end
|
|
1838
|
+
if has_attachments?
|
|
1839
|
+
parts.delete_if { |p| p.attachment? }
|
|
1826
1840
|
|
|
1827
|
-
|
|
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
|
|
1828
1845
|
|
|
1829
1846
|
self
|
|
1830
1847
|
end
|
|
@@ -1857,7 +1874,7 @@ module Mail
|
|
|
1857
1874
|
case
|
|
1858
1875
|
when k == 'delivery_handler'
|
|
1859
1876
|
begin
|
|
1860
|
-
m.delivery_handler = Object.const_get(v) unless
|
|
1877
|
+
m.delivery_handler = Object.const_get(v) unless Utilities.blank?(v)
|
|
1861
1878
|
rescue NameError
|
|
1862
1879
|
end
|
|
1863
1880
|
when k == 'transport_encoding'
|
|
@@ -1967,7 +1984,7 @@ module Mail
|
|
|
1967
1984
|
|
|
1968
1985
|
private
|
|
1969
1986
|
|
|
1970
|
-
HEADER_SEPARATOR = /#{CRLF}#{CRLF}
|
|
1987
|
+
HEADER_SEPARATOR = /#{Constants::CRLF}#{Constants::CRLF}/
|
|
1971
1988
|
|
|
1972
1989
|
# 2.1. General Description
|
|
1973
1990
|
# A message consists of header fields (collectively called "the header
|
|
@@ -1976,9 +1993,6 @@ module Mail
|
|
|
1976
1993
|
# this standard. The body is simply a sequence of characters that
|
|
1977
1994
|
# follows the header and is separated from the header by an empty line
|
|
1978
1995
|
# (i.e., a line with nothing preceding the CRLF).
|
|
1979
|
-
#
|
|
1980
|
-
# Additionally, I allow for the case where someone might have put whitespace
|
|
1981
|
-
# on the "gap line"
|
|
1982
1996
|
def parse_message
|
|
1983
1997
|
header_part, body_part = raw_source.lstrip.split(HEADER_SEPARATOR, 2)
|
|
1984
1998
|
self.header = header_part
|
|
@@ -1986,8 +2000,7 @@ module Mail
|
|
|
1986
2000
|
end
|
|
1987
2001
|
|
|
1988
2002
|
def raw_source=(value)
|
|
1989
|
-
|
|
1990
|
-
@raw_source = value.to_crlf
|
|
2003
|
+
@raw_source = value
|
|
1991
2004
|
end
|
|
1992
2005
|
|
|
1993
2006
|
# see comments to body=. We take data and process it lazily
|
|
@@ -1999,11 +2012,9 @@ module Mail
|
|
|
1999
2012
|
@body_raw = nil
|
|
2000
2013
|
add_encoding_to_body
|
|
2001
2014
|
when @body && @body.multipart?
|
|
2002
|
-
|
|
2003
|
-
add_encoding_to_body
|
|
2015
|
+
self.text_part = value
|
|
2004
2016
|
else
|
|
2005
2017
|
@body_raw = value
|
|
2006
|
-
# process_body_raw
|
|
2007
2018
|
end
|
|
2008
2019
|
end
|
|
2009
2020
|
|
|
@@ -2018,9 +2029,9 @@ module Mail
|
|
|
2018
2029
|
|
|
2019
2030
|
def set_envelope_header
|
|
2020
2031
|
raw_string = raw_source.to_s
|
|
2021
|
-
if match_data =
|
|
2032
|
+
if match_data = raw_string.match(/\AFrom\s(#{TEXT}+)#{Constants::CRLF}/m)
|
|
2022
2033
|
set_envelope(match_data[1])
|
|
2023
|
-
self.raw_source = raw_string.sub(match_data[0], "")
|
|
2034
|
+
self.raw_source = raw_string.sub(match_data[0], "")
|
|
2024
2035
|
end
|
|
2025
2036
|
end
|
|
2026
2037
|
|
|
@@ -2028,6 +2039,13 @@ module Mail
|
|
|
2028
2039
|
body.split!(boundary)
|
|
2029
2040
|
end
|
|
2030
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
|
+
|
|
2031
2049
|
def add_encoding_to_body
|
|
2032
2050
|
if has_content_transfer_encoding?
|
|
2033
2051
|
@body.encoding = content_transfer_encoding
|
|
@@ -2035,18 +2053,18 @@ module Mail
|
|
|
2035
2053
|
end
|
|
2036
2054
|
|
|
2037
2055
|
def identify_and_set_transfer_encoding
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
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
|
|
2043
2061
|
end
|
|
2044
2062
|
|
|
2045
2063
|
def add_required_fields
|
|
2046
2064
|
add_required_message_fields
|
|
2047
2065
|
add_multipart_mixed_header if body.multipart?
|
|
2048
2066
|
add_content_type unless has_content_type?
|
|
2049
|
-
add_charset
|
|
2067
|
+
add_charset if text? && !has_charset?
|
|
2050
2068
|
add_content_transfer_encoding unless has_content_transfer_encoding?
|
|
2051
2069
|
end
|
|
2052
2070
|
|
|
@@ -2122,10 +2140,10 @@ module Mail
|
|
|
2122
2140
|
content_disp_name = header[:content_disposition].filename rescue nil
|
|
2123
2141
|
content_loc_name = header[:content_location].location rescue nil
|
|
2124
2142
|
case
|
|
2125
|
-
when content_type && content_type_name
|
|
2126
|
-
filename = content_type_name
|
|
2127
2143
|
when content_disposition && content_disp_name
|
|
2128
2144
|
filename = content_disp_name
|
|
2145
|
+
when content_type && content_type_name
|
|
2146
|
+
filename = content_type_name
|
|
2129
2147
|
when content_location && content_loc_name
|
|
2130
2148
|
filename = content_loc_name
|
|
2131
2149
|
else
|
|
@@ -2146,20 +2164,7 @@ module Mail
|
|
|
2146
2164
|
end
|
|
2147
2165
|
|
|
2148
2166
|
def decode_body_as_text
|
|
2149
|
-
|
|
2150
|
-
if charset
|
|
2151
|
-
if RUBY_VERSION < '1.9'
|
|
2152
|
-
require 'iconv'
|
|
2153
|
-
return Iconv.conv("UTF-8//TRANSLIT//IGNORE", charset, body_text)
|
|
2154
|
-
else
|
|
2155
|
-
if encoding = Encoding.find(charset) rescue nil
|
|
2156
|
-
body_text.force_encoding(encoding)
|
|
2157
|
-
return body_text.encode(Encoding::UTF_8, :undef => :replace, :invalid => :replace, :replace => '')
|
|
2158
|
-
end
|
|
2159
|
-
end
|
|
2160
|
-
end
|
|
2161
|
-
body_text
|
|
2167
|
+
Encodings.transcode_charset decode_body, charset, 'UTF-8'
|
|
2162
2168
|
end
|
|
2163
|
-
|
|
2164
2169
|
end
|
|
2165
2170
|
end
|
data/lib/mail/multibyte/chars.rb
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
require 'mail/multibyte/unicode'
|
|
2
4
|
|
|
3
5
|
module Mail #:nodoc:
|
|
4
6
|
module Multibyte #:nodoc:
|
|
@@ -39,7 +41,7 @@ module Mail #:nodoc:
|
|
|
39
41
|
if RUBY_VERSION >= "1.9"
|
|
40
42
|
# Creates a new Chars instance by wrapping _string_.
|
|
41
43
|
def initialize(string)
|
|
42
|
-
@wrapped_string = string
|
|
44
|
+
@wrapped_string = string.dup
|
|
43
45
|
@wrapped_string.force_encoding(Encoding::UTF_8) unless @wrapped_string.frozen?
|
|
44
46
|
end
|
|
45
47
|
else
|
|
@@ -102,7 +104,7 @@ module Mail #:nodoc:
|
|
|
102
104
|
# Returns a new Chars object containing the _other_ object concatenated to the string.
|
|
103
105
|
#
|
|
104
106
|
# Example:
|
|
105
|
-
# ('Café'
|
|
107
|
+
# (Mail::Multibyte.mb_chars('Café') + ' périferôl').to_s # => "Café périferôl"
|
|
106
108
|
def +(other)
|
|
107
109
|
chars(@wrapped_string + other)
|
|
108
110
|
end
|
|
@@ -110,7 +112,7 @@ module Mail #:nodoc:
|
|
|
110
112
|
# Like <tt>String#=~</tt> only it returns the character offset (in codepoints) instead of the byte offset.
|
|
111
113
|
#
|
|
112
114
|
# Example:
|
|
113
|
-
# 'Café périferôl'
|
|
115
|
+
# Mail::Multibyte.mb_chars('Café périferôl') =~ /ô/ # => 12
|
|
114
116
|
def =~(other)
|
|
115
117
|
translate_offset(@wrapped_string =~ other)
|
|
116
118
|
end
|
|
@@ -118,7 +120,7 @@ module Mail #:nodoc:
|
|
|
118
120
|
# Inserts the passed string at specified codepoint offsets.
|
|
119
121
|
#
|
|
120
122
|
# Example:
|
|
121
|
-
# 'Café'.
|
|
123
|
+
# Mail::Multibyte.mb_chars('Café').insert(4, ' périferôl').to_s # => "Café périferôl"
|
|
122
124
|
def insert(offset, fragment)
|
|
123
125
|
unpacked = Unicode.u_unpack(@wrapped_string)
|
|
124
126
|
unless offset > unpacked.length
|
|
@@ -134,7 +136,7 @@ module Mail #:nodoc:
|
|
|
134
136
|
# Returns +true+ if contained string contains _other_. Returns +false+ otherwise.
|
|
135
137
|
#
|
|
136
138
|
# Example:
|
|
137
|
-
# 'Café'.
|
|
139
|
+
# Mail::Multibyte.mb_chars('Café').include?('é') # => true
|
|
138
140
|
def include?(other)
|
|
139
141
|
# We have to redefine this method because Enumerable defines it.
|
|
140
142
|
@wrapped_string.include?(other)
|
|
@@ -143,8 +145,8 @@ module Mail #:nodoc:
|
|
|
143
145
|
# Returns the position _needle_ in the string, counting in codepoints. Returns +nil+ if _needle_ isn't found.
|
|
144
146
|
#
|
|
145
147
|
# Example:
|
|
146
|
-
# 'Café périferôl'.
|
|
147
|
-
# 'Café périferôl'.
|
|
148
|
+
# Mail::Multibyte.mb_chars('Café périferôl').index('ô') # => 12
|
|
149
|
+
# Mail::Multibyte.mb_chars('Café périferôl').index(/\w/u) # => 0
|
|
148
150
|
def index(needle, offset=0)
|
|
149
151
|
wrapped_offset = first(offset).wrapped_string.length
|
|
150
152
|
index = @wrapped_string.index(needle, wrapped_offset)
|
|
@@ -156,8 +158,8 @@ module Mail #:nodoc:
|
|
|
156
158
|
# string. Returns +nil+ if _needle_ isn't found.
|
|
157
159
|
#
|
|
158
160
|
# Example:
|
|
159
|
-
# 'Café périferôl'.
|
|
160
|
-
# 'Café périferôl'.
|
|
161
|
+
# Mail::Multibyte.mb_chars('Café périferôl').rindex('é') # => 6
|
|
162
|
+
# Mail::Multibyte.mb_chars('Café périferôl').rindex(/\w/u) # => 13
|
|
161
163
|
def rindex(needle, offset=nil)
|
|
162
164
|
offset ||= length
|
|
163
165
|
wrapped_offset = first(offset).wrapped_string.length
|
|
@@ -189,7 +191,7 @@ module Mail #:nodoc:
|
|
|
189
191
|
# Returns the codepoint of the first character in the string.
|
|
190
192
|
#
|
|
191
193
|
# Example:
|
|
192
|
-
# 'こんにちは'.
|
|
194
|
+
# Mail::Multibyte.mb_chars('こんにちは').ord # => 12371
|
|
193
195
|
def ord
|
|
194
196
|
Unicode.u_unpack(@wrapped_string)[0]
|
|
195
197
|
end
|
|
@@ -198,10 +200,10 @@ module Mail #:nodoc:
|
|
|
198
200
|
#
|
|
199
201
|
# Example:
|
|
200
202
|
#
|
|
201
|
-
# "¾ cup".
|
|
203
|
+
# Mail::Multibyte.mb_chars("¾ cup").rjust(8).to_s
|
|
202
204
|
# # => " ¾ cup"
|
|
203
205
|
#
|
|
204
|
-
# "¾ cup".
|
|
206
|
+
# Mail::Multibyte.mb_chars("¾ cup").rjust(8, " ").to_s # Use non-breaking whitespace
|
|
205
207
|
# # => " ¾ cup"
|
|
206
208
|
def rjust(integer, padstr=' ')
|
|
207
209
|
justify(integer, :right, padstr)
|
|
@@ -211,10 +213,10 @@ module Mail #:nodoc:
|
|
|
211
213
|
#
|
|
212
214
|
# Example:
|
|
213
215
|
#
|
|
214
|
-
# "¾ cup".
|
|
216
|
+
# Mail::Multibyte.mb_chars("¾ cup").rjust(8).to_s
|
|
215
217
|
# # => "¾ cup "
|
|
216
218
|
#
|
|
217
|
-
# "¾ cup".
|
|
219
|
+
# Mail::Multibyte.mb_chars("¾ cup").rjust(8, " ").to_s # Use non-breaking whitespace
|
|
218
220
|
# # => "¾ cup "
|
|
219
221
|
def ljust(integer, padstr=' ')
|
|
220
222
|
justify(integer, :left, padstr)
|
|
@@ -224,10 +226,10 @@ module Mail #:nodoc:
|
|
|
224
226
|
#
|
|
225
227
|
# Example:
|
|
226
228
|
#
|
|
227
|
-
# "¾ cup".
|
|
229
|
+
# Mail::Multibyte.mb_chars("¾ cup").center(8).to_s
|
|
228
230
|
# # => " ¾ cup "
|
|
229
231
|
#
|
|
230
|
-
# "¾ cup".
|
|
232
|
+
# Mail::Multibyte.mb_chars("¾ cup").center(8, " ").to_s # Use non-breaking whitespace
|
|
231
233
|
# # => " ¾ cup "
|
|
232
234
|
def center(integer, padstr=' ')
|
|
233
235
|
justify(integer, :center, padstr)
|
|
@@ -243,7 +245,7 @@ module Mail #:nodoc:
|
|
|
243
245
|
# instances instead of String. This makes chaining methods easier.
|
|
244
246
|
#
|
|
245
247
|
# Example:
|
|
246
|
-
# 'Café périferôl'.
|
|
248
|
+
# Mail::Multibyte.mb_chars('Café périferôl').split(/é/).map { |part| part.upcase.to_s } # => ["CAF", " P", "RIFERÔL"]
|
|
247
249
|
def split(*args)
|
|
248
250
|
@wrapped_string.split(*args).map { |i| i.mb_chars }
|
|
249
251
|
end
|
|
@@ -268,12 +270,12 @@ module Mail #:nodoc:
|
|
|
268
270
|
@wrapped_string[*args] = replace_by
|
|
269
271
|
else
|
|
270
272
|
result = Unicode.u_unpack(@wrapped_string)
|
|
271
|
-
if args[0].is_a?(
|
|
273
|
+
if args[0].is_a?(Integer)
|
|
272
274
|
raise IndexError, "index #{args[0]} out of string" if args[0] >= result.length
|
|
273
275
|
min = args[0]
|
|
274
276
|
max = args[1].nil? ? min : (min + args[1] - 1)
|
|
275
277
|
range = Range.new(min, max)
|
|
276
|
-
replace_by = [replace_by].pack('U') if replace_by.is_a?(
|
|
278
|
+
replace_by = [replace_by].pack('U') if replace_by.is_a?(Integer)
|
|
277
279
|
elsif args.first.is_a?(Range)
|
|
278
280
|
raise RangeError, "#{args[0]} out of range" if args[0].min >= result.length
|
|
279
281
|
range = args[0]
|
|
@@ -291,7 +293,7 @@ module Mail #:nodoc:
|
|
|
291
293
|
# Reverses all characters in the string.
|
|
292
294
|
#
|
|
293
295
|
# Example:
|
|
294
|
-
# 'Café'.
|
|
296
|
+
# Mail::Multibyte.mb_chars('Café').reverse.to_s # => 'éfaC'
|
|
295
297
|
def reverse
|
|
296
298
|
chars(Unicode.g_unpack(@wrapped_string).reverse.flatten.pack('U*'))
|
|
297
299
|
end
|
|
@@ -300,7 +302,7 @@ module Mail #:nodoc:
|
|
|
300
302
|
# character.
|
|
301
303
|
#
|
|
302
304
|
# Example:
|
|
303
|
-
# 'こんにちは'.
|
|
305
|
+
# Mail::Multibyte.mb_chars('こんにちは').slice(2..3).to_s # => "にち"
|
|
304
306
|
def slice(*args)
|
|
305
307
|
if args.size > 2
|
|
306
308
|
raise ArgumentError, "wrong number of arguments (#{args.size} for 1)" # Do as if we were native
|
|
@@ -337,7 +339,7 @@ module Mail #:nodoc:
|
|
|
337
339
|
# Convert characters in the string to uppercase.
|
|
338
340
|
#
|
|
339
341
|
# Example:
|
|
340
|
-
# 'Laurent, où sont les tests ?'.
|
|
342
|
+
# Mail::Multibyte.mb_chars('Laurent, où sont les tests ?').upcase.to_s # => "LAURENT, OÙ SONT LES TESTS ?"
|
|
341
343
|
def upcase
|
|
342
344
|
chars(Unicode.apply_mapping(@wrapped_string, :uppercase_mapping))
|
|
343
345
|
end
|
|
@@ -345,7 +347,7 @@ module Mail #:nodoc:
|
|
|
345
347
|
# Convert characters in the string to lowercase.
|
|
346
348
|
#
|
|
347
349
|
# Example:
|
|
348
|
-
# 'VĚDA A VÝZKUM'.
|
|
350
|
+
# Mail::Multibyte.mb_chars('VĚDA A VÝZKUM').downcase.to_s # => "věda a výzkum"
|
|
349
351
|
def downcase
|
|
350
352
|
chars(Unicode.apply_mapping(@wrapped_string, :lowercase_mapping))
|
|
351
353
|
end
|
|
@@ -353,7 +355,7 @@ module Mail #:nodoc:
|
|
|
353
355
|
# Converts the first character to uppercase and the remainder to lowercase.
|
|
354
356
|
#
|
|
355
357
|
# Example:
|
|
356
|
-
# 'über'.
|
|
358
|
+
# Mail::Multibyte.mb_chars('über').capitalize.to_s # => "Über"
|
|
357
359
|
def capitalize
|
|
358
360
|
(slice(0) || chars('')).upcase + (slice(1..-1) || chars('')).downcase
|
|
359
361
|
end
|
|
@@ -361,8 +363,8 @@ module Mail #:nodoc:
|
|
|
361
363
|
# Capitalizes the first letter of every word, when possible.
|
|
362
364
|
#
|
|
363
365
|
# Example:
|
|
364
|
-
# "ÉL QUE SE ENTERÓ".
|
|
365
|
-
# "日本語".
|
|
366
|
+
# Mail::Multibyte.mb_chars("ÉL QUE SE ENTERÓ").titleize # => "Él Que Se Enteró"
|
|
367
|
+
# Mail::Multibyte.mb_chars("日本語").titleize # => "日本語"
|
|
366
368
|
def titleize
|
|
367
369
|
chars(downcase.to_s.gsub(/\b('?\S)/u) { Unicode.apply_mapping $1, :uppercase_mapping })
|
|
368
370
|
end
|
|
@@ -382,7 +384,7 @@ module Mail #:nodoc:
|
|
|
382
384
|
#
|
|
383
385
|
# Example:
|
|
384
386
|
# 'é'.length # => 2
|
|
385
|
-
# 'é'.
|
|
387
|
+
# Mail::Multibyte.mb_chars('é').decompose.to_s.length # => 3
|
|
386
388
|
def decompose
|
|
387
389
|
chars(Unicode.decompose_codepoints(:canonical, Unicode.u_unpack(@wrapped_string)).pack('U*'))
|
|
388
390
|
end
|
|
@@ -391,7 +393,7 @@ module Mail #:nodoc:
|
|
|
391
393
|
#
|
|
392
394
|
# Example:
|
|
393
395
|
# 'é'.length # => 3
|
|
394
|
-
# 'é'.
|
|
396
|
+
# Mail::Multibyte.mb_chars('é').compose.to_s.length # => 2
|
|
395
397
|
def compose
|
|
396
398
|
chars(Unicode.compose_codepoints(Unicode.u_unpack(@wrapped_string)).pack('U*'))
|
|
397
399
|
end
|
|
@@ -399,8 +401,8 @@ module Mail #:nodoc:
|
|
|
399
401
|
# Returns the number of grapheme clusters in the string.
|
|
400
402
|
#
|
|
401
403
|
# Example:
|
|
402
|
-
# 'क्षि'.
|
|
403
|
-
# 'क्षि'.
|
|
404
|
+
# Mail::Multibyte.mb_chars('क्षि').length # => 4
|
|
405
|
+
# Mail::Multibyte.mb_chars('क्षि').g_length # => 3
|
|
404
406
|
def g_length
|
|
405
407
|
Unicode.g_unpack(@wrapped_string).length
|
|
406
408
|
end
|