mail 2.7.1 → 2.9.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 +4 -4
- data/README.md +135 -59
- data/lib/mail/attachments_list.rb +5 -6
- data/lib/mail/body.rb +51 -74
- data/lib/mail/constants.rb +27 -5
- data/lib/mail/elements/address.rb +27 -27
- data/lib/mail/elements/address_list.rb +1 -1
- data/lib/mail/elements/content_disposition_element.rb +1 -1
- data/lib/mail/elements/content_location_element.rb +1 -1
- data/lib/mail/elements/content_transfer_encoding_element.rb +1 -1
- data/lib/mail/elements/content_type_element.rb +8 -4
- data/lib/mail/elements/date_time_element.rb +1 -1
- data/lib/mail/elements/envelope_from_element.rb +13 -7
- data/lib/mail/elements/message_ids_element.rb +14 -5
- data/lib/mail/elements/mime_version_element.rb +1 -1
- data/lib/mail/elements/phrase_list.rb +7 -2
- data/lib/mail/elements/received_element.rb +20 -6
- data/lib/mail/encodings/7bit.rb +5 -0
- data/lib/mail/encodings/base64.rb +2 -2
- data/lib/mail/encodings/quoted_printable.rb +2 -2
- data/lib/mail/encodings/unix_to_unix.rb +1 -1
- data/lib/mail/encodings.rb +32 -61
- data/lib/mail/envelope.rb +11 -14
- data/lib/mail/field.rb +100 -86
- data/lib/mail/field_list.rb +61 -8
- data/lib/mail/fields/bcc_field.rb +33 -52
- data/lib/mail/fields/cc_field.rb +27 -49
- data/lib/mail/fields/comments_field.rb +26 -37
- data/lib/mail/fields/common_address_field.rb +162 -0
- data/lib/mail/fields/common_date_field.rb +56 -0
- data/lib/mail/fields/common_field.rb +77 -0
- data/lib/mail/fields/common_message_id_field.rb +41 -0
- data/lib/mail/fields/content_description_field.rb +6 -14
- data/lib/mail/fields/content_disposition_field.rb +11 -38
- data/lib/mail/fields/content_id_field.rb +23 -51
- data/lib/mail/fields/content_location_field.rb +10 -25
- data/lib/mail/fields/content_transfer_encoding_field.rb +30 -31
- data/lib/mail/fields/content_type_field.rb +49 -75
- data/lib/mail/fields/date_field.rb +22 -51
- data/lib/mail/fields/from_field.rb +27 -49
- data/lib/mail/fields/in_reply_to_field.rb +37 -49
- data/lib/mail/fields/keywords_field.rb +17 -31
- data/lib/mail/fields/message_id_field.rb +24 -71
- data/lib/mail/fields/mime_version_field.rb +18 -30
- data/lib/mail/fields/named_structured_field.rb +10 -0
- data/lib/mail/fields/named_unstructured_field.rb +10 -0
- data/lib/mail/fields/optional_field.rb +5 -7
- data/lib/mail/fields/{common/parameter_hash.rb → parameter_hash.rb} +12 -10
- data/lib/mail/fields/received_field.rb +42 -57
- data/lib/mail/fields/references_field.rb +34 -49
- data/lib/mail/fields/reply_to_field.rb +27 -49
- data/lib/mail/fields/resent_bcc_field.rb +27 -49
- data/lib/mail/fields/resent_cc_field.rb +27 -49
- data/lib/mail/fields/resent_date_field.rb +4 -29
- data/lib/mail/fields/resent_from_field.rb +27 -49
- data/lib/mail/fields/resent_message_id_field.rb +4 -29
- data/lib/mail/fields/resent_sender_field.rb +26 -56
- data/lib/mail/fields/resent_to_field.rb +27 -49
- data/lib/mail/fields/return_path_field.rb +49 -54
- data/lib/mail/fields/sender_field.rb +33 -55
- data/lib/mail/fields/structured_field.rb +2 -30
- data/lib/mail/fields/subject_field.rb +8 -11
- data/lib/mail/fields/to_field.rb +27 -49
- data/lib/mail/fields/unstructured_field.rb +15 -48
- data/lib/mail/fields.rb +9 -0
- data/lib/mail/header.rb +69 -110
- data/lib/mail/mail.rb +32 -27
- data/lib/mail/matchers/attachment_matchers.rb +15 -0
- data/lib/mail/message.rb +67 -78
- data/lib/mail/multibyte/chars.rb +9 -167
- data/lib/mail/multibyte/unicode.rb +1 -1
- data/lib/mail/multibyte/utils.rb +26 -43
- data/lib/mail/multibyte.rb +1 -11
- data/lib/mail/network/delivery_methods/exim.rb +5 -4
- data/lib/mail/network/delivery_methods/file_delivery.rb +13 -12
- data/lib/mail/network/delivery_methods/logger_delivery.rb +2 -5
- data/lib/mail/network/delivery_methods/sendmail.rb +27 -35
- data/lib/mail/network/delivery_methods/smtp.rb +81 -30
- data/lib/mail/network/delivery_methods/smtp_connection.rb +10 -19
- data/lib/mail/network/delivery_methods/test_mailer.rb +8 -6
- data/lib/mail/network/retriever_methods/base.rb +13 -13
- data/lib/mail/network/retriever_methods/imap.rb +8 -5
- data/lib/mail/network/retriever_methods/pop3.rb +22 -22
- data/lib/mail/network/retriever_methods/test_retriever.rb +2 -1
- data/lib/mail/parsers/address_lists_parser.rb +33073 -33064
- data/lib/mail/parsers/address_lists_parser.rl +11 -0
- data/lib/mail/parsers/content_disposition_parser.rb +837 -828
- data/lib/mail/parsers/content_disposition_parser.rl +11 -0
- data/lib/mail/parsers/content_location_parser.rb +773 -764
- data/lib/mail/parsers/content_location_parser.rl +12 -1
- data/lib/mail/parsers/content_transfer_encoding_parser.rb +477 -468
- data/lib/mail/parsers/content_transfer_encoding_parser.rl +11 -0
- data/lib/mail/parsers/content_type_parser.rb +974 -965
- data/lib/mail/parsers/content_type_parser.rl +11 -0
- data/lib/mail/parsers/date_time_parser.rb +841 -832
- data/lib/mail/parsers/date_time_parser.rl +11 -0
- data/lib/mail/parsers/envelope_from_parser.rb +3627 -3530
- data/lib/mail/parsers/envelope_from_parser.rl +11 -0
- data/lib/mail/parsers/message_ids_parser.rb +5110 -2800
- data/lib/mail/parsers/message_ids_parser.rl +16 -1
- data/lib/mail/parsers/mime_version_parser.rb +466 -457
- data/lib/mail/parsers/mime_version_parser.rl +11 -0
- data/lib/mail/parsers/phrase_lists_parser.rb +839 -830
- data/lib/mail/parsers/phrase_lists_parser.rl +12 -1
- data/lib/mail/parsers/received_parser.rb +8692 -8683
- data/lib/mail/parsers/received_parser.rl +11 -0
- data/lib/mail/parsers/rfc5322.rl +28 -13
- data/lib/mail/parsers.rb +11 -17
- data/lib/mail/part.rb +24 -28
- data/lib/mail/parts_list.rb +57 -0
- data/lib/mail/smtp_envelope.rb +57 -0
- data/lib/mail/utilities.rb +321 -69
- data/lib/mail/version.rb +1 -1
- data/lib/mail/yaml.rb +30 -0
- data/lib/mail.rb +2 -22
- metadata +83 -19
- data/lib/mail/check_delivery_params.rb +0 -60
- data/lib/mail/core_extensions/smtp.rb +0 -28
- data/lib/mail/core_extensions/string.rb +0 -17
- data/lib/mail/fields/common/address_container.rb +0 -17
- data/lib/mail/fields/common/common_address.rb +0 -161
- data/lib/mail/fields/common/common_date.rb +0 -36
- data/lib/mail/fields/common/common_field.rb +0 -52
- data/lib/mail/fields/common/common_message_id.rb +0 -49
- data/lib/mail/version_specific/ruby_1_8.rb +0 -163
- data/lib/mail/version_specific/ruby_1_9.rb +0 -278
data/lib/mail/message.rb
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
2
|
# frozen_string_literal: true
|
|
3
|
-
require
|
|
3
|
+
require 'mail/constants'
|
|
4
|
+
require 'mail/utilities'
|
|
5
|
+
require 'mail/yaml'
|
|
4
6
|
|
|
5
7
|
module Mail
|
|
6
8
|
# The Message class provides a single point of access to all things to do with an
|
|
@@ -46,10 +48,6 @@ module Mail
|
|
|
46
48
|
# follows the header and is separated from the header by an empty line
|
|
47
49
|
# (i.e., a line with nothing preceding the CRLF).
|
|
48
50
|
class Message
|
|
49
|
-
|
|
50
|
-
include Constants
|
|
51
|
-
include Utilities
|
|
52
|
-
|
|
53
51
|
# ==Making an email
|
|
54
52
|
#
|
|
55
53
|
# You can make an new mail object via a block, passing a string, file or direct assignment.
|
|
@@ -149,7 +147,7 @@ module Mail
|
|
|
149
147
|
# m.to 'recipient@example.com'
|
|
150
148
|
# end
|
|
151
149
|
if block_given?
|
|
152
|
-
if block.arity.zero?
|
|
150
|
+
if block.arity.zero?
|
|
153
151
|
instance_eval(&block)
|
|
154
152
|
else
|
|
155
153
|
yield self
|
|
@@ -235,11 +233,6 @@ module Mail
|
|
|
235
233
|
def self.default_charset=(charset); @@default_charset = charset; end
|
|
236
234
|
self.default_charset = 'UTF-8'
|
|
237
235
|
|
|
238
|
-
def register_for_delivery_notification(observer)
|
|
239
|
-
warn("Message#register_for_delivery_notification is deprecated, please call Mail.register_observer instead")
|
|
240
|
-
Mail.register_observer(observer)
|
|
241
|
-
end
|
|
242
|
-
|
|
243
236
|
def inform_observers
|
|
244
237
|
Mail.inform_observers(self)
|
|
245
238
|
end
|
|
@@ -301,7 +294,7 @@ module Mail
|
|
|
301
294
|
reply.references ||= bracketed_message_id
|
|
302
295
|
end
|
|
303
296
|
if subject
|
|
304
|
-
reply.subject =
|
|
297
|
+
reply.subject = /^Re:/i.match?(subject) ? subject : "Re: #{subject}"
|
|
305
298
|
end
|
|
306
299
|
if reply_to || from
|
|
307
300
|
reply.to = self[reply_to ? :reply_to : :from].to_s
|
|
@@ -356,6 +349,10 @@ module Mail
|
|
|
356
349
|
# the same content, ignoring the Message-ID field, unless BOTH emails have a defined and
|
|
357
350
|
# different Message-ID value, then they are false.
|
|
358
351
|
#
|
|
352
|
+
# Note that Mail creates Date and Mime-Type fields if they don't exist.
|
|
353
|
+
# The Date field is derived from the current time, so this needs to be allowed for in comparisons.
|
|
354
|
+
# (Mime-type does not depend on dynamic data, so cannot affect equality)
|
|
355
|
+
#
|
|
359
356
|
# So, in practice the == operator works like this:
|
|
360
357
|
#
|
|
361
358
|
# m1 = Mail.new("Subject: Hello\r\n\r\nHello")
|
|
@@ -377,14 +374,17 @@ module Mail
|
|
|
377
374
|
# m1 = Mail.new("Message-ID: <1234@test>\r\nSubject: Hello\r\n\r\nHello")
|
|
378
375
|
# m2 = Mail.new("Message-ID: <DIFFERENT@test>\r\nSubject: Hello\r\n\r\nHello")
|
|
379
376
|
# m1 == m2 #=> false
|
|
380
|
-
def ==(other)
|
|
377
|
+
def ==(other) # TODO could be more efficient
|
|
381
378
|
return false unless other.respond_to?(:encoded)
|
|
382
379
|
|
|
380
|
+
stamp = Mail::CommonDateField.normalize_datetime('')
|
|
381
|
+
# Note: must always dup the inputs so they are not altered by encoded
|
|
383
382
|
if self.message_id && other.message_id
|
|
384
|
-
|
|
383
|
+
dup.tap { |m| m.date ||= stamp }.encoded ==
|
|
384
|
+
other.dup.tap { |m| m.date ||= stamp }.encoded
|
|
385
385
|
else
|
|
386
|
-
dup.tap { |m| m.message_id = '<temp@test>' }.encoded ==
|
|
387
|
-
other.dup.tap { |m| m.message_id = '<temp@test>' }.encoded
|
|
386
|
+
dup.tap { |m| m.message_id = '<temp@test>'; m.date ||= stamp }.encoded ==
|
|
387
|
+
other.dup.tap { |m| m.message_id = '<temp@test>'; m.date ||= stamp }.encoded
|
|
388
388
|
end
|
|
389
389
|
end
|
|
390
390
|
|
|
@@ -406,9 +406,9 @@ module Mail
|
|
|
406
406
|
end
|
|
407
407
|
|
|
408
408
|
# Sets the envelope from for the email
|
|
409
|
-
def set_envelope(
|
|
409
|
+
def set_envelope(val)
|
|
410
410
|
@raw_envelope = val
|
|
411
|
-
@envelope = Mail::Envelope.
|
|
411
|
+
@envelope = Mail::Envelope.parse(val) rescue nil
|
|
412
412
|
end
|
|
413
413
|
|
|
414
414
|
# The raw_envelope is the From mikel@test.lindsaar.net Mon May 2 16:07:05 2009
|
|
@@ -471,7 +471,7 @@ module Mail
|
|
|
471
471
|
# message.errors.size #=> 1
|
|
472
472
|
# message.errors.first[0] #=> "Content-Transfer-Encoding"
|
|
473
473
|
# message.errors.first[1] #=> "weirdo"
|
|
474
|
-
# message.errors.first[
|
|
474
|
+
# message.errors.first[2] #=> <The original error message exception>
|
|
475
475
|
#
|
|
476
476
|
# This is a good first defence on detecting spam by the way. Some spammers send
|
|
477
477
|
# invalid emails to try and get email parsers to give up parsing them.
|
|
@@ -1323,7 +1323,7 @@ module Mail
|
|
|
1323
1323
|
def []=(name, value)
|
|
1324
1324
|
if name.to_s == 'body'
|
|
1325
1325
|
self.body = value
|
|
1326
|
-
elsif
|
|
1326
|
+
elsif /content[-_]type/i.match?(name.to_s)
|
|
1327
1327
|
header[name] = value
|
|
1328
1328
|
elsif name.to_s == 'charset'
|
|
1329
1329
|
self.charset = value
|
|
@@ -1339,7 +1339,7 @@ module Mail
|
|
|
1339
1339
|
# mail['foo'] = '1234'
|
|
1340
1340
|
# mail['foo'].to_s #=> '1234'
|
|
1341
1341
|
def [](name)
|
|
1342
|
-
header[underscoreize(name)]
|
|
1342
|
+
header[Utilities.underscoreize(name)]
|
|
1343
1343
|
end
|
|
1344
1344
|
|
|
1345
1345
|
# Method Missing in this implementation allows you to set any of the
|
|
@@ -1385,7 +1385,7 @@ module Mail
|
|
|
1385
1385
|
#:nodoc:
|
|
1386
1386
|
# Only take the structured fields, as we could take _anything_ really
|
|
1387
1387
|
# as it could become an optional field... "but therin lies the dark side"
|
|
1388
|
-
field_name = underscoreize(name).chomp("=")
|
|
1388
|
+
field_name = Utilities.underscoreize(name).chomp("=")
|
|
1389
1389
|
if Mail::Field::KNOWN_FIELDS.include?(field_name)
|
|
1390
1390
|
if args.empty?
|
|
1391
1391
|
header[field_name]
|
|
@@ -1416,7 +1416,7 @@ module Mail
|
|
|
1416
1416
|
header.has_date?
|
|
1417
1417
|
end
|
|
1418
1418
|
|
|
1419
|
-
# Returns true if the message has a
|
|
1419
|
+
# Returns true if the message has a MIME-Version field, the field may or may
|
|
1420
1420
|
# not have a value, but the field exists or not.
|
|
1421
1421
|
def has_mime_version?
|
|
1422
1422
|
header.has_mime_version?
|
|
@@ -1436,11 +1436,6 @@ module Mail
|
|
|
1436
1436
|
header[:content_transfer_encoding] && Utilities.blank?(header[:content_transfer_encoding].errors)
|
|
1437
1437
|
end
|
|
1438
1438
|
|
|
1439
|
-
def has_transfer_encoding? # :nodoc:
|
|
1440
|
-
warn(":has_transfer_encoding? is deprecated in Mail 1.4.3. Please use has_content_transfer_encoding?\n#{caller}")
|
|
1441
|
-
has_content_transfer_encoding?
|
|
1442
|
-
end
|
|
1443
|
-
|
|
1444
1439
|
# Creates a new empty Message-ID field and inserts it in the correct order
|
|
1445
1440
|
# into the Header. The MessageIdField object will automatically generate
|
|
1446
1441
|
# a unique message ID if you try and encode it or output it to_s without
|
|
@@ -1489,7 +1484,9 @@ module Mail
|
|
|
1489
1484
|
warning = "Non US-ASCII detected and no charset defined.\nDefaulting to UTF-8, set your own if this is incorrect.\n"
|
|
1490
1485
|
warn(warning)
|
|
1491
1486
|
end
|
|
1492
|
-
|
|
1487
|
+
if @charset
|
|
1488
|
+
header[:content_type].parameters['charset'] = @charset
|
|
1489
|
+
end
|
|
1493
1490
|
end
|
|
1494
1491
|
end
|
|
1495
1492
|
|
|
@@ -1498,30 +1495,15 @@ module Mail
|
|
|
1498
1495
|
header[:content_transfer_encoding] ||= body.default_encoding
|
|
1499
1496
|
end
|
|
1500
1497
|
|
|
1501
|
-
def add_transfer_encoding # :nodoc:
|
|
1502
|
-
warn(":add_transfer_encoding is deprecated in Mail 1.4.3. Please use add_content_transfer_encoding\n#{caller}")
|
|
1503
|
-
add_content_transfer_encoding
|
|
1504
|
-
end
|
|
1505
|
-
|
|
1506
|
-
def transfer_encoding # :nodoc:
|
|
1507
|
-
warn(":transfer_encoding is deprecated in Mail 1.4.3. Please use content_transfer_encoding\n#{caller}")
|
|
1508
|
-
content_transfer_encoding
|
|
1509
|
-
end
|
|
1510
|
-
|
|
1511
1498
|
# Returns the MIME media type of part we are on, this is taken from the content-type header
|
|
1512
1499
|
def mime_type
|
|
1513
1500
|
has_content_type? ? header[:content_type].string : nil rescue nil
|
|
1514
1501
|
end
|
|
1515
1502
|
|
|
1516
|
-
def message_content_type
|
|
1517
|
-
warn(":message_content_type is deprecated in Mail 1.4.3. Please use mime_type\n#{caller}")
|
|
1518
|
-
mime_type
|
|
1519
|
-
end
|
|
1520
|
-
|
|
1521
1503
|
# Returns the character set defined in the content type field
|
|
1522
1504
|
def charset
|
|
1523
1505
|
if @header
|
|
1524
|
-
has_content_type? ? content_type_parameters['charset'] : @charset
|
|
1506
|
+
has_content_type? && !multipart? ? content_type_parameters['charset'] : @charset
|
|
1525
1507
|
else
|
|
1526
1508
|
@charset
|
|
1527
1509
|
end
|
|
@@ -1544,12 +1526,6 @@ module Mail
|
|
|
1544
1526
|
has_content_type? ? header[:content_type].sub_type : nil rescue nil
|
|
1545
1527
|
end
|
|
1546
1528
|
|
|
1547
|
-
# Returns the content type parameters
|
|
1548
|
-
def mime_parameters
|
|
1549
|
-
warn(':mime_parameters is deprecated in Mail 1.4.3, please use :content_type_parameters instead')
|
|
1550
|
-
content_type_parameters
|
|
1551
|
-
end
|
|
1552
|
-
|
|
1553
1529
|
# Returns the content type parameters
|
|
1554
1530
|
def content_type_parameters
|
|
1555
1531
|
has_content_type? ? header[:content_type].parameters : nil rescue nil
|
|
@@ -1557,17 +1533,17 @@ module Mail
|
|
|
1557
1533
|
|
|
1558
1534
|
# Returns true if the message is multipart
|
|
1559
1535
|
def multipart?
|
|
1560
|
-
has_content_type? ?
|
|
1536
|
+
has_content_type? ? /^multipart$/i.match?(main_type) : false
|
|
1561
1537
|
end
|
|
1562
1538
|
|
|
1563
1539
|
# Returns true if the message is a multipart/report
|
|
1564
1540
|
def multipart_report?
|
|
1565
|
-
multipart? &&
|
|
1541
|
+
multipart? && /^report$/i.match?(sub_type)
|
|
1566
1542
|
end
|
|
1567
1543
|
|
|
1568
1544
|
# Returns true if the message is a multipart/report; report-type=delivery-status;
|
|
1569
1545
|
def delivery_status_report?
|
|
1570
|
-
multipart_report? && content_type_parameters['report-type']
|
|
1546
|
+
multipart_report? && /^delivery-status$/i.match?(content_type_parameters['report-type'])
|
|
1571
1547
|
end
|
|
1572
1548
|
|
|
1573
1549
|
# returns the part in a multipart/report email that has the content-type delivery-status
|
|
@@ -1798,12 +1774,22 @@ module Mail
|
|
|
1798
1774
|
self.attachments[basename] = filedata
|
|
1799
1775
|
end
|
|
1800
1776
|
|
|
1777
|
+
MULTIPART_CONVERSION_CONTENT_FIELDS = [ :content_description, :content_disposition, :content_transfer_encoding, :content_type ]
|
|
1778
|
+
private_constant :MULTIPART_CONVERSION_CONTENT_FIELDS if respond_to?(:private_constant)
|
|
1779
|
+
|
|
1801
1780
|
def convert_to_multipart
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1781
|
+
text_part = Mail::Part.new(:content_type => 'text/plain;', :body => body.decoded)
|
|
1782
|
+
|
|
1783
|
+
MULTIPART_CONVERSION_CONTENT_FIELDS.each do |field_name|
|
|
1784
|
+
if value = send(field_name)
|
|
1785
|
+
writer = :"#{field_name}="
|
|
1786
|
+
text_part.send writer, value
|
|
1787
|
+
send writer, nil
|
|
1788
|
+
end
|
|
1789
|
+
end
|
|
1806
1790
|
text_part.charset = charset unless @defaulted_charset
|
|
1791
|
+
|
|
1792
|
+
self.body = ''
|
|
1807
1793
|
self.body << text_part
|
|
1808
1794
|
end
|
|
1809
1795
|
|
|
@@ -1818,11 +1804,6 @@ module Mail
|
|
|
1818
1804
|
add_required_fields
|
|
1819
1805
|
end
|
|
1820
1806
|
|
|
1821
|
-
def encode!
|
|
1822
|
-
warn("Deprecated in 1.1.0 in favour of :ready_to_send! as it is less confusing with encoding and decoding.")
|
|
1823
|
-
ready_to_send!
|
|
1824
|
-
end
|
|
1825
|
-
|
|
1826
1807
|
# Outputs an encoded string representation of the mail message including
|
|
1827
1808
|
# all headers, attachments, etc. This is an encoded email in US-ASCII,
|
|
1828
1809
|
# so it is able to be directly sent to an email server.
|
|
@@ -1836,7 +1817,7 @@ module Mail
|
|
|
1836
1817
|
|
|
1837
1818
|
def without_attachments!
|
|
1838
1819
|
if has_attachments?
|
|
1839
|
-
parts.
|
|
1820
|
+
parts.delete_attachments
|
|
1840
1821
|
|
|
1841
1822
|
reencoded = parts.empty? ? '' : body.encoded(content_transfer_encoding)
|
|
1842
1823
|
@body = nil # So the new parts won't be added to the existing body
|
|
@@ -1867,7 +1848,7 @@ module Mail
|
|
|
1867
1848
|
end
|
|
1868
1849
|
|
|
1869
1850
|
def self.from_yaml(str)
|
|
1870
|
-
hash = YAML.load(str)
|
|
1851
|
+
hash = Mail::YAML.load(str)
|
|
1871
1852
|
m = self.new(:headers => hash['headers'])
|
|
1872
1853
|
hash.delete('headers')
|
|
1873
1854
|
hash.each do |k,v|
|
|
@@ -1881,7 +1862,7 @@ module Mail
|
|
|
1881
1862
|
m.transport_encoding(v)
|
|
1882
1863
|
when k == 'multipart_body'
|
|
1883
1864
|
v.map {|part| m.add_part Mail::Part.from_yaml(part) }
|
|
1884
|
-
when k
|
|
1865
|
+
when k.start_with?('@')
|
|
1885
1866
|
m.instance_variable_set(k.to_sym, v)
|
|
1886
1867
|
end
|
|
1887
1868
|
end
|
|
@@ -1900,6 +1881,15 @@ module Mail
|
|
|
1900
1881
|
"#<#{self.class}:#{self.object_id}, Multipart: #{multipart?}, Headers: #{header.field_summary}>"
|
|
1901
1882
|
end
|
|
1902
1883
|
|
|
1884
|
+
def inspect_structure
|
|
1885
|
+
inspect +
|
|
1886
|
+
if self.multipart?
|
|
1887
|
+
"\n" + parts.inspect_structure
|
|
1888
|
+
else
|
|
1889
|
+
''
|
|
1890
|
+
end
|
|
1891
|
+
end
|
|
1892
|
+
|
|
1903
1893
|
def decoded
|
|
1904
1894
|
case
|
|
1905
1895
|
when self.text?
|
|
@@ -1909,7 +1899,7 @@ module Mail
|
|
|
1909
1899
|
when !self.multipart?
|
|
1910
1900
|
body.decoded
|
|
1911
1901
|
else
|
|
1912
|
-
raise NoMethodError, '
|
|
1902
|
+
raise NoMethodError, 'This message cannot be decoded as _entire_ message, try calling #decoded on the various fields and body or parts if it is a multipart message.'
|
|
1913
1903
|
end
|
|
1914
1904
|
end
|
|
1915
1905
|
|
|
@@ -1979,12 +1969,12 @@ module Mail
|
|
|
1979
1969
|
end
|
|
1980
1970
|
|
|
1981
1971
|
def text?
|
|
1982
|
-
has_content_type? ?
|
|
1972
|
+
has_content_type? ? /^text$/i.match?(main_type) : false
|
|
1983
1973
|
end
|
|
1984
1974
|
|
|
1985
1975
|
private
|
|
1986
1976
|
|
|
1987
|
-
HEADER_SEPARATOR = /#{Constants::
|
|
1977
|
+
HEADER_SEPARATOR = /#{Constants::LAX_CRLF}#{Constants::LAX_CRLF}/
|
|
1988
1978
|
|
|
1989
1979
|
# 2.1. General Description
|
|
1990
1980
|
# A message consists of header fields (collectively called "the header
|
|
@@ -2029,7 +2019,7 @@ module Mail
|
|
|
2029
2019
|
|
|
2030
2020
|
def set_envelope_header
|
|
2031
2021
|
raw_string = raw_source.to_s
|
|
2032
|
-
if match_data = raw_string.match(/\AFrom\s(#{TEXT}
|
|
2022
|
+
if match_data = raw_string.match(/\AFrom\s+([^:\s]#{Constants::TEXT}*)#{Constants::LAX_CRLF}/m)
|
|
2033
2023
|
set_envelope(match_data[1])
|
|
2034
2024
|
self.raw_source = raw_string.sub(match_data[0], "")
|
|
2035
2025
|
end
|
|
@@ -2053,10 +2043,12 @@ module Mail
|
|
|
2053
2043
|
end
|
|
2054
2044
|
|
|
2055
2045
|
def identify_and_set_transfer_encoding
|
|
2056
|
-
if body
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2046
|
+
if body
|
|
2047
|
+
if body.multipart?
|
|
2048
|
+
self.content_transfer_encoding = @transport_encoding
|
|
2049
|
+
else
|
|
2050
|
+
self.content_transfer_encoding = body.negotiate_best_encoding(@transport_encoding, allowed_encodings).to_s
|
|
2051
|
+
end
|
|
2060
2052
|
end
|
|
2061
2053
|
end
|
|
2062
2054
|
|
|
@@ -2076,7 +2068,6 @@ module Mail
|
|
|
2076
2068
|
|
|
2077
2069
|
def add_multipart_alternate_header
|
|
2078
2070
|
header['content-type'] = ContentTypeField.with_boundary('multipart/alternative').value
|
|
2079
|
-
header['content_type'].parameters[:charset] = @charset
|
|
2080
2071
|
body.boundary = boundary
|
|
2081
2072
|
end
|
|
2082
2073
|
|
|
@@ -2084,7 +2075,6 @@ module Mail
|
|
|
2084
2075
|
unless body.boundary && boundary
|
|
2085
2076
|
header['content-type'] = 'multipart/mixed' unless header['content-type']
|
|
2086
2077
|
header['content-type'].parameters[:boundary] = ContentTypeField.generate_boundary
|
|
2087
|
-
header['content_type'].parameters[:charset] = @charset
|
|
2088
2078
|
body.boundary = boundary
|
|
2089
2079
|
end
|
|
2090
2080
|
end
|
|
@@ -2092,7 +2082,6 @@ module Mail
|
|
|
2092
2082
|
def add_multipart_mixed_header
|
|
2093
2083
|
unless header['content-type']
|
|
2094
2084
|
header['content-type'] = ContentTypeField.with_boundary('multipart/mixed').value
|
|
2095
|
-
header['content_type'].parameters[:charset] = @charset
|
|
2096
2085
|
body.boundary = boundary
|
|
2097
2086
|
end
|
|
2098
2087
|
end
|
|
@@ -2109,7 +2098,7 @@ module Mail
|
|
|
2109
2098
|
body_content = nil
|
|
2110
2099
|
|
|
2111
2100
|
passed_in_options.each_pair do |k,v|
|
|
2112
|
-
k = underscoreize(k).to_sym if k.class == String
|
|
2101
|
+
k = Utilities.underscoreize(k).to_sym if k.class == String
|
|
2113
2102
|
if k == :headers
|
|
2114
2103
|
self.headers(v)
|
|
2115
2104
|
elsif k == :body
|
data/lib/mail/multibyte/chars.rb
CHANGED
|
@@ -38,21 +38,15 @@ module Mail #:nodoc:
|
|
|
38
38
|
alias to_s wrapped_string
|
|
39
39
|
alias to_str wrapped_string
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
@wrapped_string.force_encoding(Encoding::UTF_8) unless @wrapped_string.frozen?
|
|
46
|
-
end
|
|
47
|
-
else
|
|
48
|
-
def initialize(string) #:nodoc:
|
|
49
|
-
@wrapped_string = string
|
|
50
|
-
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?
|
|
51
45
|
end
|
|
52
46
|
|
|
53
47
|
# Forward all undefined methods to the wrapped string.
|
|
54
48
|
def method_missing(method, *args, &block)
|
|
55
|
-
if method.to_s
|
|
49
|
+
if method.to_s.end_with?('!')
|
|
56
50
|
@wrapped_string.__send__(method, *args, &block)
|
|
57
51
|
self
|
|
58
52
|
else
|
|
@@ -72,15 +66,6 @@ module Mail #:nodoc:
|
|
|
72
66
|
true
|
|
73
67
|
end
|
|
74
68
|
|
|
75
|
-
# Returns +true+ when the proxy class can handle the string. Returns +false+ otherwise.
|
|
76
|
-
def self.consumes?(string)
|
|
77
|
-
# Unpack is a little bit faster than regular expressions.
|
|
78
|
-
string.unpack('U*')
|
|
79
|
-
true
|
|
80
|
-
rescue ArgumentError
|
|
81
|
-
false
|
|
82
|
-
end
|
|
83
|
-
|
|
84
69
|
include Comparable
|
|
85
70
|
|
|
86
71
|
# Returns -1, 0, or 1, depending on whether the Chars object is to be sorted before,
|
|
@@ -94,151 +79,8 @@ module Mail #:nodoc:
|
|
|
94
79
|
@wrapped_string <=> other.to_s
|
|
95
80
|
end
|
|
96
81
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
# +false+ otherwise.
|
|
100
|
-
def self.wants?(string)
|
|
101
|
-
$KCODE == 'UTF8' && consumes?(string)
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
# Returns a new Chars object containing the _other_ object concatenated to the string.
|
|
105
|
-
#
|
|
106
|
-
# Example:
|
|
107
|
-
# (Mail::Multibyte.mb_chars('Café') + ' périferôl').to_s # => "Café périferôl"
|
|
108
|
-
def +(other)
|
|
109
|
-
chars(@wrapped_string + other)
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
# Like <tt>String#=~</tt> only it returns the character offset (in codepoints) instead of the byte offset.
|
|
113
|
-
#
|
|
114
|
-
# Example:
|
|
115
|
-
# Mail::Multibyte.mb_chars('Café périferôl') =~ /ô/ # => 12
|
|
116
|
-
def =~(other)
|
|
117
|
-
translate_offset(@wrapped_string =~ other)
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
# Inserts the passed string at specified codepoint offsets.
|
|
121
|
-
#
|
|
122
|
-
# Example:
|
|
123
|
-
# Mail::Multibyte.mb_chars('Café').insert(4, ' périferôl').to_s # => "Café périferôl"
|
|
124
|
-
def insert(offset, fragment)
|
|
125
|
-
unpacked = Unicode.u_unpack(@wrapped_string)
|
|
126
|
-
unless offset > unpacked.length
|
|
127
|
-
@wrapped_string.replace(
|
|
128
|
-
Unicode.u_unpack(@wrapped_string).insert(offset, *Unicode.u_unpack(fragment)).pack('U*')
|
|
129
|
-
)
|
|
130
|
-
else
|
|
131
|
-
raise IndexError, "index #{offset} out of string"
|
|
132
|
-
end
|
|
133
|
-
self
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
# Returns +true+ if contained string contains _other_. Returns +false+ otherwise.
|
|
137
|
-
#
|
|
138
|
-
# Example:
|
|
139
|
-
# Mail::Multibyte.mb_chars('Café').include?('é') # => true
|
|
140
|
-
def include?(other)
|
|
141
|
-
# We have to redefine this method because Enumerable defines it.
|
|
142
|
-
@wrapped_string.include?(other)
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
# Returns the position _needle_ in the string, counting in codepoints. Returns +nil+ if _needle_ isn't found.
|
|
146
|
-
#
|
|
147
|
-
# Example:
|
|
148
|
-
# Mail::Multibyte.mb_chars('Café périferôl').index('ô') # => 12
|
|
149
|
-
# Mail::Multibyte.mb_chars('Café périferôl').index(/\w/u) # => 0
|
|
150
|
-
def index(needle, offset=0)
|
|
151
|
-
wrapped_offset = first(offset).wrapped_string.length
|
|
152
|
-
index = @wrapped_string.index(needle, wrapped_offset)
|
|
153
|
-
index ? (Unicode.u_unpack(@wrapped_string.slice(0...index)).size) : nil
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
# Returns the position _needle_ in the string, counting in
|
|
157
|
-
# codepoints, searching backward from _offset_ or the end of the
|
|
158
|
-
# string. Returns +nil+ if _needle_ isn't found.
|
|
159
|
-
#
|
|
160
|
-
# Example:
|
|
161
|
-
# Mail::Multibyte.mb_chars('Café périferôl').rindex('é') # => 6
|
|
162
|
-
# Mail::Multibyte.mb_chars('Café périferôl').rindex(/\w/u) # => 13
|
|
163
|
-
def rindex(needle, offset=nil)
|
|
164
|
-
offset ||= length
|
|
165
|
-
wrapped_offset = first(offset).wrapped_string.length
|
|
166
|
-
index = @wrapped_string.rindex(needle, wrapped_offset)
|
|
167
|
-
index ? (Unicode.u_unpack(@wrapped_string.slice(0...index)).size) : nil
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
# Returns the number of codepoints in the string
|
|
171
|
-
def size
|
|
172
|
-
Unicode.u_unpack(@wrapped_string).size
|
|
173
|
-
end
|
|
174
|
-
alias_method :length, :size
|
|
175
|
-
|
|
176
|
-
# Strips entire range of Unicode whitespace from the right of the string.
|
|
177
|
-
def rstrip
|
|
178
|
-
chars(@wrapped_string.gsub(Unicode::TRAILERS_PAT, ''))
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
# Strips entire range of Unicode whitespace from the left of the string.
|
|
182
|
-
def lstrip
|
|
183
|
-
chars(@wrapped_string.gsub(Unicode::LEADERS_PAT, ''))
|
|
184
|
-
end
|
|
185
|
-
|
|
186
|
-
# Strips entire range of Unicode whitespace from the right and left of the string.
|
|
187
|
-
def strip
|
|
188
|
-
rstrip.lstrip
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
# Returns the codepoint of the first character in the string.
|
|
192
|
-
#
|
|
193
|
-
# Example:
|
|
194
|
-
# Mail::Multibyte.mb_chars('こんにちは').ord # => 12371
|
|
195
|
-
def ord
|
|
196
|
-
Unicode.u_unpack(@wrapped_string)[0]
|
|
197
|
-
end
|
|
198
|
-
|
|
199
|
-
# Works just like <tt>String#rjust</tt>, only integer specifies characters instead of bytes.
|
|
200
|
-
#
|
|
201
|
-
# Example:
|
|
202
|
-
#
|
|
203
|
-
# Mail::Multibyte.mb_chars("¾ cup").rjust(8).to_s
|
|
204
|
-
# # => " ¾ cup"
|
|
205
|
-
#
|
|
206
|
-
# Mail::Multibyte.mb_chars("¾ cup").rjust(8, " ").to_s # Use non-breaking whitespace
|
|
207
|
-
# # => " ¾ cup"
|
|
208
|
-
def rjust(integer, padstr=' ')
|
|
209
|
-
justify(integer, :right, padstr)
|
|
210
|
-
end
|
|
211
|
-
|
|
212
|
-
# Works just like <tt>String#ljust</tt>, only integer specifies characters instead of bytes.
|
|
213
|
-
#
|
|
214
|
-
# Example:
|
|
215
|
-
#
|
|
216
|
-
# Mail::Multibyte.mb_chars("¾ cup").rjust(8).to_s
|
|
217
|
-
# # => "¾ cup "
|
|
218
|
-
#
|
|
219
|
-
# Mail::Multibyte.mb_chars("¾ cup").rjust(8, " ").to_s # Use non-breaking whitespace
|
|
220
|
-
# # => "¾ cup "
|
|
221
|
-
def ljust(integer, padstr=' ')
|
|
222
|
-
justify(integer, :left, padstr)
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
# Works just like <tt>String#center</tt>, only integer specifies characters instead of bytes.
|
|
226
|
-
#
|
|
227
|
-
# Example:
|
|
228
|
-
#
|
|
229
|
-
# Mail::Multibyte.mb_chars("¾ cup").center(8).to_s
|
|
230
|
-
# # => " ¾ cup "
|
|
231
|
-
#
|
|
232
|
-
# Mail::Multibyte.mb_chars("¾ cup").center(8, " ").to_s # Use non-breaking whitespace
|
|
233
|
-
# # => " ¾ cup "
|
|
234
|
-
def center(integer, padstr=' ')
|
|
235
|
-
justify(integer, :center, padstr)
|
|
236
|
-
end
|
|
237
|
-
|
|
238
|
-
else
|
|
239
|
-
def =~(other)
|
|
240
|
-
@wrapped_string =~ other
|
|
241
|
-
end
|
|
82
|
+
def =~(other)
|
|
83
|
+
@wrapped_string =~ other
|
|
242
84
|
end
|
|
243
85
|
|
|
244
86
|
# Works just like <tt>String#split</tt>, with the exception that the items in the resulting list are Chars
|
|
@@ -331,7 +173,7 @@ module Mail #:nodoc:
|
|
|
331
173
|
#
|
|
332
174
|
# Example:
|
|
333
175
|
# s = 'こんにちは'
|
|
334
|
-
# s.mb_chars.limit(7) # => "
|
|
176
|
+
# s.mb_chars.limit(7) # => "こん"
|
|
335
177
|
def limit(limit)
|
|
336
178
|
slice(0...translate_offset(limit))
|
|
337
179
|
end
|
|
@@ -419,7 +261,7 @@ module Mail #:nodoc:
|
|
|
419
261
|
# exclude lstrip!, rstrip! and strip! because they are already work as expected on multibyte strings.
|
|
420
262
|
if public_method_defined?(method)
|
|
421
263
|
define_method("#{method}!") do |*args|
|
|
422
|
-
@wrapped_string = send(
|
|
264
|
+
@wrapped_string = send(method, *args).to_s
|
|
423
265
|
self
|
|
424
266
|
end
|
|
425
267
|
end
|