mail 2.5.2 → 2.6.4
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 +7 -0
- data/CHANGELOG.rdoc +124 -0
- data/CONTRIBUTING.md +21 -6
- data/Dependencies.txt +0 -1
- data/Gemfile +7 -22
- data/MIT-LICENSE +20 -0
- data/README.md +86 -31
- data/Rakefile +11 -22
- data/lib/mail/attachments_list.rb +4 -2
- data/lib/mail/body.rb +36 -11
- data/lib/mail/check_delivery_params.rb +13 -22
- data/lib/mail/configuration.rb +1 -0
- data/lib/mail/{patterns.rb → constants.rb} +29 -6
- data/lib/mail/core_extensions/smtp.rb +13 -13
- data/lib/mail/core_extensions/string/access.rb +1 -0
- data/lib/mail/core_extensions/string/multibyte.rb +1 -0
- data/lib/mail/core_extensions/string.rb +5 -17
- data/lib/mail/elements/address.rb +86 -145
- data/lib/mail/elements/address_list.rb +10 -42
- data/lib/mail/elements/content_disposition_element.rb +4 -7
- data/lib/mail/elements/content_location_element.rb +8 -11
- data/lib/mail/elements/content_transfer_encoding_element.rb +4 -10
- data/lib/mail/elements/content_type_element.rb +5 -8
- data/lib/mail/elements/date_time_element.rb +4 -7
- data/lib/mail/elements/envelope_from_element.rb +19 -13
- data/lib/mail/elements/message_ids_element.rb +8 -11
- data/lib/mail/elements/mime_version_element.rb +4 -7
- data/lib/mail/elements/phrase_list.rb +3 -7
- data/lib/mail/elements/received_element.rb +4 -7
- data/lib/mail/elements.rb +1 -0
- data/lib/mail/encodings/7bit.rb +1 -0
- data/lib/mail/encodings/8bit.rb +8 -2
- data/lib/mail/encodings/base64.rb +7 -1
- data/lib/mail/encodings/binary.rb +1 -0
- data/lib/mail/encodings/quoted_printable.rb +11 -4
- data/lib/mail/encodings/transfer_encoding.rb +27 -17
- data/lib/mail/encodings/unix_to_unix.rb +18 -0
- data/lib/mail/encodings.rb +54 -39
- data/lib/mail/envelope.rb +1 -5
- data/lib/mail/field.rb +81 -31
- data/lib/mail/field_list.rb +19 -18
- data/lib/mail/fields/bcc_field.rb +17 -5
- data/lib/mail/fields/cc_field.rb +3 -3
- data/lib/mail/fields/comments_field.rb +2 -1
- data/lib/mail/fields/common/address_container.rb +1 -0
- data/lib/mail/fields/common/common_address.rb +34 -28
- data/lib/mail/fields/common/common_date.rb +2 -8
- data/lib/mail/fields/common/common_field.rb +9 -5
- data/lib/mail/fields/common/common_message_id.rb +3 -2
- data/lib/mail/fields/common/parameter_hash.rb +4 -3
- data/lib/mail/fields/content_description_field.rb +1 -0
- data/lib/mail/fields/content_disposition_field.rb +13 -12
- data/lib/mail/fields/content_id_field.rb +4 -4
- data/lib/mail/fields/content_location_field.rb +2 -1
- data/lib/mail/fields/content_transfer_encoding_field.rb +4 -9
- data/lib/mail/fields/content_type_field.rb +4 -3
- data/lib/mail/fields/date_field.rb +16 -15
- data/lib/mail/fields/from_field.rb +3 -3
- data/lib/mail/fields/in_reply_to_field.rb +3 -1
- data/lib/mail/fields/keywords_field.rb +2 -2
- data/lib/mail/fields/message_id_field.rb +4 -4
- data/lib/mail/fields/mime_version_field.rb +3 -2
- data/lib/mail/fields/optional_field.rb +1 -0
- data/lib/mail/fields/received_field.rb +4 -3
- data/lib/mail/fields/references_field.rb +3 -1
- data/lib/mail/fields/reply_to_field.rb +3 -3
- data/lib/mail/fields/resent_bcc_field.rb +3 -3
- data/lib/mail/fields/resent_cc_field.rb +3 -3
- data/lib/mail/fields/resent_date_field.rb +2 -1
- data/lib/mail/fields/resent_from_field.rb +3 -3
- data/lib/mail/fields/resent_message_id_field.rb +1 -0
- data/lib/mail/fields/resent_sender_field.rb +4 -4
- data/lib/mail/fields/resent_to_field.rb +3 -3
- data/lib/mail/fields/return_path_field.rb +1 -1
- data/lib/mail/fields/sender_field.rb +9 -9
- data/lib/mail/fields/structured_field.rb +1 -0
- data/lib/mail/fields/subject_field.rb +1 -0
- data/lib/mail/fields/to_field.rb +3 -3
- data/lib/mail/fields/unstructured_field.rb +15 -8
- data/lib/mail/fields.rb +1 -0
- data/lib/mail/header.rb +21 -29
- data/lib/mail/indifferent_hash.rb +1 -0
- data/lib/mail/mail.rb +16 -1
- data/lib/mail/matchers/attachment_matchers.rb +29 -0
- data/lib/mail/matchers/has_sent_mail.rb +61 -4
- data/lib/mail/message.rb +175 -72
- data/lib/mail/multibyte/chars.rb +3 -2
- data/lib/mail/multibyte/exceptions.rb +1 -0
- data/lib/mail/multibyte/unicode.rb +22 -15
- data/lib/mail/multibyte/utils.rb +1 -0
- data/lib/mail/multibyte.rb +1 -0
- data/lib/mail/network/delivery_methods/exim.rb +3 -7
- data/lib/mail/network/delivery_methods/file_delivery.rb +2 -1
- data/lib/mail/network/delivery_methods/sendmail.rb +33 -10
- data/lib/mail/network/delivery_methods/smtp.rb +32 -34
- data/lib/mail/network/delivery_methods/smtp_connection.rb +7 -6
- data/lib/mail/network/delivery_methods/test_mailer.rb +3 -2
- data/lib/mail/network/retriever_methods/base.rb +1 -0
- data/lib/mail/network/retriever_methods/imap.rb +19 -13
- data/lib/mail/network/retriever_methods/pop3.rb +1 -0
- data/lib/mail/network/retriever_methods/test_retriever.rb +1 -0
- data/lib/mail/network.rb +1 -0
- data/lib/mail/parsers/address_lists_parser.rb +133 -0
- data/lib/mail/parsers/content_disposition_parser.rb +68 -0
- data/lib/mail/parsers/content_location_parser.rb +36 -0
- data/lib/mail/parsers/content_transfer_encoding_parser.rb +34 -0
- data/lib/mail/parsers/content_type_parser.rb +67 -0
- data/lib/mail/parsers/date_time_parser.rb +39 -0
- data/lib/mail/parsers/envelope_from_parser.rb +46 -0
- data/lib/mail/parsers/message_ids_parser.rb +40 -0
- data/lib/mail/parsers/mime_version_parser.rb +42 -0
- data/lib/mail/parsers/phrase_lists_parser.rb +36 -0
- data/lib/mail/parsers/ragel/common.rl +185 -0
- data/lib/mail/parsers/ragel/date_time.rl +30 -0
- data/lib/mail/parsers/ragel/parser_info.rb +61 -0
- data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +14864 -0
- data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +751 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +614 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +447 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +825 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +817 -0
- data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +2149 -0
- data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +1570 -0
- data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +440 -0
- data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +564 -0
- data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +51 -0
- data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +5144 -0
- data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +37 -0
- data/lib/mail/parsers/ragel/ruby.rb +40 -0
- data/lib/mail/parsers/ragel.rb +18 -0
- data/lib/mail/parsers/received_parser.rb +49 -0
- data/lib/mail/parsers.rb +27 -0
- data/lib/mail/part.rb +11 -4
- data/lib/mail/parts_list.rb +30 -10
- data/lib/mail/utilities.rb +127 -63
- data/lib/mail/values/unicode_tables.dat +0 -0
- data/lib/mail/version.rb +8 -15
- data/lib/mail/version_specific/ruby_1_8.rb +18 -7
- data/lib/mail/version_specific/ruby_1_9.rb +136 -32
- data/lib/mail.rb +12 -6
- metadata +115 -77
- data/lib/VERSION +0 -4
- data/lib/load_parsers.rb +0 -41
- data/lib/mail/core_extensions/nil.rb +0 -19
- data/lib/mail/core_extensions/object.rb +0 -13
- data/lib/mail/core_extensions/shell_escape.rb +0 -56
- data/lib/mail/parsers/address_lists.rb +0 -64
- data/lib/mail/parsers/address_lists.treetop +0 -19
- data/lib/mail/parsers/content_disposition.rb +0 -535
- data/lib/mail/parsers/content_disposition.treetop +0 -46
- data/lib/mail/parsers/content_location.rb +0 -139
- data/lib/mail/parsers/content_location.treetop +0 -20
- data/lib/mail/parsers/content_transfer_encoding.rb +0 -162
- data/lib/mail/parsers/content_transfer_encoding.treetop +0 -20
- data/lib/mail/parsers/content_type.rb +0 -967
- data/lib/mail/parsers/content_type.treetop +0 -68
- data/lib/mail/parsers/date_time.rb +0 -114
- data/lib/mail/parsers/date_time.treetop +0 -11
- data/lib/mail/parsers/envelope_from.rb +0 -194
- data/lib/mail/parsers/envelope_from.treetop +0 -32
- data/lib/mail/parsers/message_ids.rb +0 -45
- data/lib/mail/parsers/message_ids.treetop +0 -15
- data/lib/mail/parsers/mime_version.rb +0 -144
- data/lib/mail/parsers/mime_version.treetop +0 -19
- data/lib/mail/parsers/phrase_lists.rb +0 -45
- data/lib/mail/parsers/phrase_lists.treetop +0 -15
- data/lib/mail/parsers/received.rb +0 -71
- data/lib/mail/parsers/received.treetop +0 -11
- data/lib/mail/parsers/rfc2045.rb +0 -464
- data/lib/mail/parsers/rfc2045.treetop +0 -36
- data/lib/mail/parsers/rfc2822.rb +0 -5397
- data/lib/mail/parsers/rfc2822.treetop +0 -410
- data/lib/mail/parsers/rfc2822_obsolete.rb +0 -3768
- data/lib/mail/parsers/rfc2822_obsolete.treetop +0 -241
- data/lib/tasks/corpus.rake +0 -125
- data/lib/tasks/treetop.rake +0 -10
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
%%{
|
|
2
|
+
machine received;
|
|
3
|
+
|
|
4
|
+
include rb_actions "rb_actions.rl";
|
|
5
|
+
include common "../../common.rl";
|
|
6
|
+
|
|
7
|
+
getkey data_unpacked[p];
|
|
8
|
+
|
|
9
|
+
main := received;
|
|
10
|
+
}%%
|
|
11
|
+
|
|
12
|
+
module Mail
|
|
13
|
+
module Parsers
|
|
14
|
+
module Ragel
|
|
15
|
+
module ReceivedMachine
|
|
16
|
+
%%write data;
|
|
17
|
+
|
|
18
|
+
def self.parse(data)
|
|
19
|
+
p = 0
|
|
20
|
+
eof = data.length
|
|
21
|
+
stack = []
|
|
22
|
+
|
|
23
|
+
actions = []
|
|
24
|
+
data_unpacked = data.bytes.to_a
|
|
25
|
+
%%write init;
|
|
26
|
+
%%write exec;
|
|
27
|
+
|
|
28
|
+
if p == eof && cs >= %%{ write first_final; }%%
|
|
29
|
+
return actions, nil
|
|
30
|
+
else
|
|
31
|
+
return [], "Only able to parse up to #{data[0..p]}"
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
%%{
|
|
2
|
+
machine <%= parser_name %>;
|
|
3
|
+
|
|
4
|
+
include rb_actions "rb_actions.rl";
|
|
5
|
+
include common "../../common.rl";
|
|
6
|
+
|
|
7
|
+
getkey data_unpacked[p];
|
|
8
|
+
|
|
9
|
+
main := <%= parser_name %>;
|
|
10
|
+
}%%
|
|
11
|
+
|
|
12
|
+
module Mail
|
|
13
|
+
module Parsers
|
|
14
|
+
module Ragel
|
|
15
|
+
module <%= parser_name_cc %>
|
|
16
|
+
%%write data;
|
|
17
|
+
|
|
18
|
+
def self.parse(data)
|
|
19
|
+
p = 0
|
|
20
|
+
eof = data.length
|
|
21
|
+
stack = []
|
|
22
|
+
|
|
23
|
+
actions = []
|
|
24
|
+
data_unpacked = data.bytes.to_a
|
|
25
|
+
%%write init;
|
|
26
|
+
%%write exec;
|
|
27
|
+
|
|
28
|
+
if p == eof && cs >= %%{ write first_final; }%%
|
|
29
|
+
return actions, nil
|
|
30
|
+
else
|
|
31
|
+
return [], "Only able to parse up to #{data[0..p]}"
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
module Mail
|
|
2
|
+
module Parsers
|
|
3
|
+
module Ragel
|
|
4
|
+
module Ruby
|
|
5
|
+
def self.silence_warnings
|
|
6
|
+
old, $VERBOSE = $VERBOSE, nil
|
|
7
|
+
yield
|
|
8
|
+
ensure
|
|
9
|
+
$VERBOSE = old
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Ragel-generated parsers give a lot of warnings
|
|
13
|
+
# and may cause logs to balloon in size
|
|
14
|
+
silence_warnings do
|
|
15
|
+
Mail::Parsers::Ragel::FIELD_PARSERS.each do |field_parser|
|
|
16
|
+
require "mail/parsers/ragel/ruby/machines/#{field_parser}_machine"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
MACHINE_LIST = {
|
|
21
|
+
:address_lists => AddressListsMachine,
|
|
22
|
+
:phrase_lists => PhraseListsMachine,
|
|
23
|
+
:date_time => DateTimeMachine,
|
|
24
|
+
:received => ReceivedMachine,
|
|
25
|
+
:message_ids => MessageIdsMachine,
|
|
26
|
+
:envelope_from => EnvelopeFromMachine,
|
|
27
|
+
:mime_version => MimeVersionMachine,
|
|
28
|
+
:content_type => ContentTypeMachine,
|
|
29
|
+
:content_disposition => ContentDispositionMachine,
|
|
30
|
+
:content_transfer_encoding => ContentTransferEncodingMachine,
|
|
31
|
+
:content_location => ContentLocationMachine
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
def self.parse(machine, string)
|
|
35
|
+
MACHINE_LIST[machine].parse(string)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Mail
|
|
3
|
+
module Parsers
|
|
4
|
+
module Ragel
|
|
5
|
+
require 'mail/parsers/ragel/parser_info'
|
|
6
|
+
require "mail/parsers/ragel/ruby"
|
|
7
|
+
|
|
8
|
+
def self.parse(machine, string)
|
|
9
|
+
@machine_module ||= Ruby
|
|
10
|
+
@machine_module.parse(machine, string)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.machine_module=(m)
|
|
14
|
+
@machine_module = m
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Mail::Parsers
|
|
3
|
+
class ReceivedParser
|
|
4
|
+
|
|
5
|
+
def parse(s)
|
|
6
|
+
raise Mail::Field::ParseError.new(Mail::ReceivedElement, s, 'nil is invalid') if s.nil?
|
|
7
|
+
actions, error = Ragel.parse(:received, s)
|
|
8
|
+
if error
|
|
9
|
+
raise Mail::Field::ParseError.new(Mail::ReceivedElement, s, error)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
received = ReceivedStruct.new
|
|
13
|
+
|
|
14
|
+
received_tokens_s = date_s = time_s = nil
|
|
15
|
+
actions.each_slice(2) do |action_id, p|
|
|
16
|
+
action = Mail::Parsers::Ragel::ACTIONS[action_id]
|
|
17
|
+
case action
|
|
18
|
+
|
|
19
|
+
# Received Tokens:
|
|
20
|
+
when :received_tokens_s then received_tokens_s = p
|
|
21
|
+
when :received_tokens_e
|
|
22
|
+
received.info = s[received_tokens_s..(p-1)]
|
|
23
|
+
|
|
24
|
+
# Date
|
|
25
|
+
when :date_s then date_s = p
|
|
26
|
+
when :date_e
|
|
27
|
+
received.date = s[date_s..(p-1)].strip
|
|
28
|
+
|
|
29
|
+
# Time
|
|
30
|
+
when :time_s then time_s = p
|
|
31
|
+
when :time_e
|
|
32
|
+
received.time = s[time_s..(p-1)]
|
|
33
|
+
|
|
34
|
+
when :angle_addr_s, :comment_e, :comment_s,
|
|
35
|
+
:domain_e, :domain_s, :local_dot_atom_e,
|
|
36
|
+
:local_dot_atom_pre_comment_e,
|
|
37
|
+
:local_dot_atom_pre_comment_s,
|
|
38
|
+
:local_dot_atom_s, :qstr_e, :qstr_s,
|
|
39
|
+
:local_quoted_string_s, :local_quoted_string_e
|
|
40
|
+
# ignored actions
|
|
41
|
+
|
|
42
|
+
else
|
|
43
|
+
raise Mail::Field::ParseError.new(Mail::ReceivedElement, s, "Failed to process unknown action: #{action}")
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
received
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
data/lib/mail/parsers.rb
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Mail
|
|
3
|
+
module Parsers
|
|
4
|
+
|
|
5
|
+
# Low-level ragel based parsers
|
|
6
|
+
require 'mail/parsers/ragel'
|
|
7
|
+
|
|
8
|
+
AddressListStruct = Struct.new(:addresses, :group_names, :error)
|
|
9
|
+
AddressStruct = Struct.new(:raw, :domain, :comments, :local,
|
|
10
|
+
:obs_domain_list, :display_name, :group, :error)
|
|
11
|
+
ContentDispositionStruct = Struct.new(:disposition_type, :parameters, :error)
|
|
12
|
+
ContentLocationStruct = Struct.new(:location, :error)
|
|
13
|
+
ContentTransferEncodingStruct = Struct.new(:encoding, :error)
|
|
14
|
+
ContentTypeStruct = Struct.new(:main_type, :sub_type, :parameters, :error)
|
|
15
|
+
DateTimeStruct = Struct.new(:date_string, :time_string, :error)
|
|
16
|
+
EnvelopeFromStruct = Struct.new(:address, :ctime_date, :error)
|
|
17
|
+
MessageIdsStruct = Struct.new(:message_ids, :error)
|
|
18
|
+
MimeVersionStruct = Struct.new(:major, :minor, :error)
|
|
19
|
+
PhraseListsStruct = Struct.new(:phrases, :error)
|
|
20
|
+
ReceivedStruct = Struct.new(:date, :time, :info, :error)
|
|
21
|
+
|
|
22
|
+
require 'mail/parsers/ragel/parser_info'
|
|
23
|
+
Ragel::FIELD_PARSERS.each do |field_parser|
|
|
24
|
+
require "mail/parsers/#{field_parser}_parser"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
data/lib/mail/part.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
module Mail
|
|
3
4
|
class Part < Message
|
|
4
5
|
|
|
@@ -34,12 +35,16 @@ module Mail
|
|
|
34
35
|
end
|
|
35
36
|
|
|
36
37
|
def inline?
|
|
37
|
-
header[:content_disposition].disposition_type == 'inline' if header[:content_disposition]
|
|
38
|
+
header[:content_disposition].disposition_type == 'inline' if header[:content_disposition].respond_to?(:disposition_type)
|
|
38
39
|
end
|
|
39
40
|
|
|
40
41
|
def add_required_fields
|
|
41
|
-
add_content_id unless has_content_id?
|
|
42
42
|
super
|
|
43
|
+
add_content_id if !has_content_id? && inline?
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def add_required_message_fields
|
|
47
|
+
# Override so we don't add Date, MIME-Version, or Message-ID.
|
|
43
48
|
end
|
|
44
49
|
|
|
45
50
|
def delivery_status_report_part?
|
|
@@ -90,8 +95,10 @@ module Mail
|
|
|
90
95
|
def get_return_values(key)
|
|
91
96
|
if delivery_status_data[key].is_a?(Array)
|
|
92
97
|
delivery_status_data[key].map { |a| a.value }
|
|
93
|
-
|
|
98
|
+
elsif !delivery_status_data[key].nil?
|
|
94
99
|
delivery_status_data[key].value
|
|
100
|
+
else
|
|
101
|
+
nil
|
|
95
102
|
end
|
|
96
103
|
end
|
|
97
104
|
|
|
@@ -113,4 +120,4 @@ module Mail
|
|
|
113
120
|
|
|
114
121
|
end
|
|
115
122
|
|
|
116
|
-
end
|
|
123
|
+
end
|
data/lib/mail/parts_list.rb
CHANGED
|
@@ -1,8 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'delegate'
|
|
3
|
+
|
|
1
4
|
module Mail
|
|
2
|
-
class PartsList < Array
|
|
5
|
+
class PartsList < DelegateClass(Array)
|
|
6
|
+
attr_reader :parts
|
|
7
|
+
|
|
8
|
+
def initialize(*args)
|
|
9
|
+
@parts = Array.new(*args)
|
|
10
|
+
super @parts
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# The #encode_with and #to_yaml methods are just implemented
|
|
14
|
+
# for the sake of backward compatibility ; the delegator does
|
|
15
|
+
# not correctly delegate these calls to the delegated object
|
|
16
|
+
def encode_with(coder) # :nodoc:
|
|
17
|
+
coder.represent_object(nil, @parts)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def to_yaml(options = {}) # :nodoc:
|
|
21
|
+
@parts.to_yaml(options)
|
|
22
|
+
end
|
|
3
23
|
|
|
4
24
|
def attachments
|
|
5
|
-
Mail::AttachmentsList.new(
|
|
25
|
+
Mail::AttachmentsList.new(@parts)
|
|
6
26
|
end
|
|
7
27
|
|
|
8
28
|
def collect
|
|
@@ -14,8 +34,6 @@ module Mail
|
|
|
14
34
|
to_a
|
|
15
35
|
end
|
|
16
36
|
end
|
|
17
|
-
|
|
18
|
-
undef :map
|
|
19
37
|
alias_method :map, :collect
|
|
20
38
|
|
|
21
39
|
def map!
|
|
@@ -27,24 +45,26 @@ module Mail
|
|
|
27
45
|
end
|
|
28
46
|
|
|
29
47
|
def sort
|
|
30
|
-
self.class.new(
|
|
48
|
+
self.class.new(@parts.sort)
|
|
31
49
|
end
|
|
32
50
|
|
|
33
51
|
def sort!(order)
|
|
34
|
-
|
|
52
|
+
# stable sort should be used to maintain the relative order as the parts are added
|
|
53
|
+
i = 0;
|
|
54
|
+
sorted = @parts.sort_by do |a|
|
|
35
55
|
# OK, 10000 is arbitrary... if anyone actually wants to explicitly sort 10000 parts of a
|
|
36
56
|
# single email message... please show me a use case and I'll put more work into this method,
|
|
37
57
|
# in the meantime, it works :)
|
|
38
|
-
get_order_value(a, order)
|
|
58
|
+
[get_order_value(a, order), i += 1]
|
|
39
59
|
end
|
|
40
|
-
|
|
41
|
-
sorted.each { |p|
|
|
60
|
+
@parts.clear
|
|
61
|
+
sorted.each { |p| @parts << p }
|
|
42
62
|
end
|
|
43
63
|
|
|
44
64
|
private
|
|
45
65
|
|
|
46
66
|
def get_order_value(part, order)
|
|
47
|
-
if part.respond_to?(:content_type)
|
|
67
|
+
if part.respond_to?(:content_type) && !part[:content_type].nil?
|
|
48
68
|
order.index(part[:content_type].string.downcase) || 10000
|
|
49
69
|
else
|
|
50
70
|
10000
|
data/lib/mail/utilities.rb
CHANGED
|
@@ -1,29 +1,34 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
module Mail
|
|
3
4
|
module Utilities
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
LF = "\n"
|
|
7
|
+
CRLF = "\r\n"
|
|
8
|
+
|
|
9
|
+
include Constants
|
|
10
|
+
|
|
6
11
|
# Returns true if the string supplied is free from characters not allowed as an ATOM
|
|
7
12
|
def atom_safe?( str )
|
|
8
13
|
not ATOM_UNSAFE === str
|
|
9
14
|
end
|
|
10
15
|
|
|
11
|
-
# If the string supplied has ATOM unsafe characters in it, will return the string quoted
|
|
16
|
+
# If the string supplied has ATOM unsafe characters in it, will return the string quoted
|
|
12
17
|
# in double quotes, otherwise returns the string unmodified
|
|
13
18
|
def quote_atom( str )
|
|
14
19
|
atom_safe?( str ) ? str : dquote(str)
|
|
15
20
|
end
|
|
16
21
|
|
|
17
|
-
# If the string supplied has PHRASE unsafe characters in it, will return the string quoted
|
|
22
|
+
# If the string supplied has PHRASE unsafe characters in it, will return the string quoted
|
|
18
23
|
# in double quotes, otherwise returns the string unmodified
|
|
19
24
|
def quote_phrase( str )
|
|
20
25
|
if RUBY_VERSION >= '1.9'
|
|
21
26
|
original_encoding = str.encoding
|
|
22
|
-
str.force_encoding('ASCII-8BIT')
|
|
23
|
-
if (PHRASE_UNSAFE ===
|
|
24
|
-
dquote(
|
|
27
|
+
ascii_str = str.dup.force_encoding('ASCII-8BIT')
|
|
28
|
+
if (PHRASE_UNSAFE === ascii_str)
|
|
29
|
+
dquote(ascii_str).force_encoding(original_encoding)
|
|
25
30
|
else
|
|
26
|
-
str
|
|
31
|
+
str
|
|
27
32
|
end
|
|
28
33
|
else
|
|
29
34
|
(PHRASE_UNSAFE === str) ? dquote(str) : str
|
|
@@ -35,150 +40,158 @@ module Mail
|
|
|
35
40
|
not TOKEN_UNSAFE === str
|
|
36
41
|
end
|
|
37
42
|
|
|
38
|
-
# If the string supplied has TOKEN unsafe characters in it, will return the string quoted
|
|
43
|
+
# If the string supplied has TOKEN unsafe characters in it, will return the string quoted
|
|
39
44
|
# in double quotes, otherwise returns the string unmodified
|
|
40
45
|
def quote_token( str )
|
|
41
46
|
token_safe?( str ) ? str : dquote(str)
|
|
42
47
|
end
|
|
43
48
|
|
|
44
|
-
# Wraps supplied string in double quotes
|
|
45
|
-
#
|
|
46
|
-
#
|
|
47
|
-
#
|
|
49
|
+
# Wraps supplied string in double quotes and applies \-escaping as necessary,
|
|
50
|
+
# unless it is already wrapped.
|
|
51
|
+
#
|
|
52
|
+
# Example:
|
|
53
|
+
#
|
|
54
|
+
# string = 'This is a string'
|
|
55
|
+
# dquote(string) #=> '"This is a string"'
|
|
56
|
+
#
|
|
57
|
+
# string = 'This is "a string"'
|
|
58
|
+
# dquote(string #=> '"This is \"a string\"'
|
|
48
59
|
def dquote( str )
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
# Unwraps supplied string from inside double quotes.
|
|
58
|
-
#
|
|
60
|
+
'"' + unquote(str).gsub(/[\\"]/n) {|s| '\\' + s } + '"'
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Unwraps supplied string from inside double quotes and
|
|
64
|
+
# removes any \-escaping.
|
|
65
|
+
#
|
|
59
66
|
# Example:
|
|
60
|
-
#
|
|
67
|
+
#
|
|
61
68
|
# string = '"This is a string"'
|
|
62
69
|
# unquote(string) #=> 'This is a string'
|
|
70
|
+
#
|
|
71
|
+
# string = '"This is \"a string\""'
|
|
72
|
+
# unqoute(string) #=> 'This is "a string"'
|
|
63
73
|
def unquote( str )
|
|
64
|
-
|
|
65
|
-
|
|
74
|
+
if str =~ /^"(.*?)"$/
|
|
75
|
+
$1.gsub(/\\(.)/, '\1')
|
|
76
|
+
else
|
|
77
|
+
str
|
|
78
|
+
end
|
|
66
79
|
end
|
|
67
|
-
|
|
80
|
+
|
|
68
81
|
# Wraps a string in parenthesis and escapes any that are in the string itself.
|
|
69
|
-
#
|
|
82
|
+
#
|
|
70
83
|
# Example:
|
|
71
|
-
#
|
|
84
|
+
#
|
|
72
85
|
# paren( 'This is a string' ) #=> '(This is a string)'
|
|
73
86
|
def paren( str )
|
|
74
87
|
RubyVer.paren( str )
|
|
75
88
|
end
|
|
76
|
-
|
|
89
|
+
|
|
77
90
|
# Unwraps a string from being wrapped in parenthesis
|
|
78
|
-
#
|
|
91
|
+
#
|
|
79
92
|
# Example:
|
|
80
|
-
#
|
|
93
|
+
#
|
|
81
94
|
# str = '(This is a string)'
|
|
82
95
|
# unparen( str ) #=> 'This is a string'
|
|
83
96
|
def unparen( str )
|
|
84
97
|
match = str.match(/^\((.*?)\)$/)
|
|
85
98
|
match ? match[1] : str
|
|
86
99
|
end
|
|
87
|
-
|
|
100
|
+
|
|
88
101
|
# Wraps a string in angle brackets and escapes any that are in the string itself
|
|
89
|
-
#
|
|
102
|
+
#
|
|
90
103
|
# Example:
|
|
91
|
-
#
|
|
104
|
+
#
|
|
92
105
|
# bracket( 'This is a string' ) #=> '<This is a string>'
|
|
93
106
|
def bracket( str )
|
|
94
107
|
RubyVer.bracket( str )
|
|
95
108
|
end
|
|
96
|
-
|
|
109
|
+
|
|
97
110
|
# Unwraps a string from being wrapped in parenthesis
|
|
98
|
-
#
|
|
111
|
+
#
|
|
99
112
|
# Example:
|
|
100
|
-
#
|
|
113
|
+
#
|
|
101
114
|
# str = '<This is a string>'
|
|
102
115
|
# unbracket( str ) #=> 'This is a string'
|
|
103
116
|
def unbracket( str )
|
|
104
117
|
match = str.match(/^\<(.*?)\>$/)
|
|
105
118
|
match ? match[1] : str
|
|
106
119
|
end
|
|
107
|
-
|
|
120
|
+
|
|
108
121
|
# Escape parenthesies in a string
|
|
109
|
-
#
|
|
122
|
+
#
|
|
110
123
|
# Example:
|
|
111
|
-
#
|
|
124
|
+
#
|
|
112
125
|
# str = 'This is (a) string'
|
|
113
126
|
# escape_paren( str ) #=> 'This is \(a\) string'
|
|
114
127
|
def escape_paren( str )
|
|
115
128
|
RubyVer.escape_paren( str )
|
|
116
129
|
end
|
|
117
|
-
|
|
130
|
+
|
|
118
131
|
def uri_escape( str )
|
|
119
132
|
uri_parser.escape(str)
|
|
120
133
|
end
|
|
121
|
-
|
|
134
|
+
|
|
122
135
|
def uri_unescape( str )
|
|
123
136
|
uri_parser.unescape(str)
|
|
124
137
|
end
|
|
125
|
-
|
|
138
|
+
|
|
126
139
|
def uri_parser
|
|
127
140
|
@uri_parser ||= URI.const_defined?(:Parser) ? URI::Parser.new : URI
|
|
128
141
|
end
|
|
129
|
-
|
|
142
|
+
|
|
130
143
|
# Matches two objects with their to_s values case insensitively
|
|
131
|
-
#
|
|
144
|
+
#
|
|
132
145
|
# Example:
|
|
133
|
-
#
|
|
146
|
+
#
|
|
134
147
|
# obj2 = "This_is_An_object"
|
|
135
148
|
# obj1 = :this_IS_an_object
|
|
136
149
|
# match_to_s( obj1, obj2 ) #=> true
|
|
137
150
|
def match_to_s( obj1, obj2 )
|
|
138
|
-
obj1.to_s.
|
|
151
|
+
obj1.to_s.casecmp(obj2.to_s) == 0
|
|
139
152
|
end
|
|
140
|
-
|
|
153
|
+
|
|
141
154
|
# Capitalizes a string that is joined by hyphens correctly.
|
|
142
|
-
#
|
|
155
|
+
#
|
|
143
156
|
# Example:
|
|
144
|
-
#
|
|
157
|
+
#
|
|
145
158
|
# string = 'resent-from-field'
|
|
146
159
|
# capitalize_field( string ) #=> 'Resent-From-Field'
|
|
147
160
|
def capitalize_field( str )
|
|
148
161
|
str.to_s.split("-").map { |v| v.capitalize }.join("-")
|
|
149
162
|
end
|
|
150
|
-
|
|
163
|
+
|
|
151
164
|
# Takes an underscored word and turns it into a class name
|
|
152
|
-
#
|
|
165
|
+
#
|
|
153
166
|
# Example:
|
|
154
|
-
#
|
|
167
|
+
#
|
|
155
168
|
# constantize("hello") #=> "Hello"
|
|
156
169
|
# constantize("hello-there") #=> "HelloThere"
|
|
157
170
|
# constantize("hello-there-mate") #=> "HelloThereMate"
|
|
158
171
|
def constantize( str )
|
|
159
172
|
str.to_s.split(/[-_]/).map { |v| v.capitalize }.to_s
|
|
160
173
|
end
|
|
161
|
-
|
|
174
|
+
|
|
162
175
|
# Swaps out all underscores (_) for hyphens (-) good for stringing from symbols
|
|
163
176
|
# a field name.
|
|
164
|
-
#
|
|
177
|
+
#
|
|
165
178
|
# Example:
|
|
166
|
-
#
|
|
179
|
+
#
|
|
167
180
|
# string = :resent_from_field
|
|
168
181
|
# dasherize ( string ) #=> 'resent_from_field'
|
|
169
182
|
def dasherize( str )
|
|
170
|
-
str.to_s.
|
|
183
|
+
str.to_s.tr(UNDERSCORE, HYPHEN)
|
|
171
184
|
end
|
|
172
185
|
|
|
173
186
|
# Swaps out all hyphens (-) for underscores (_) good for stringing to symbols
|
|
174
187
|
# a field name.
|
|
175
|
-
#
|
|
188
|
+
#
|
|
176
189
|
# Example:
|
|
177
|
-
#
|
|
190
|
+
#
|
|
178
191
|
# string = :resent_from_field
|
|
179
192
|
# underscoreize ( string ) #=> 'resent_from_field'
|
|
180
193
|
def underscoreize( str )
|
|
181
|
-
str.to_s.downcase.
|
|
194
|
+
str.to_s.downcase.tr(HYPHEN, UNDERSCORE)
|
|
182
195
|
end
|
|
183
196
|
|
|
184
197
|
if RUBY_VERSION <= '1.8.6'
|
|
@@ -190,7 +203,7 @@ module Mail
|
|
|
190
203
|
end
|
|
191
204
|
results
|
|
192
205
|
end
|
|
193
|
-
|
|
206
|
+
|
|
194
207
|
def map_with_index( enum, &block )
|
|
195
208
|
results = []
|
|
196
209
|
enum.each_with_index do |token, i|
|
|
@@ -198,7 +211,7 @@ module Mail
|
|
|
198
211
|
end
|
|
199
212
|
results
|
|
200
213
|
end
|
|
201
|
-
|
|
214
|
+
|
|
202
215
|
else
|
|
203
216
|
|
|
204
217
|
def map_lines( str, &block )
|
|
@@ -211,5 +224,56 @@ module Mail
|
|
|
211
224
|
|
|
212
225
|
end
|
|
213
226
|
|
|
227
|
+
# Test String#encode works correctly with line endings.
|
|
228
|
+
# Some versions of Ruby (e.g. MRI <1.9, JRuby, Rubinius) line ending
|
|
229
|
+
# normalization does not work correctly or did not have #encode.
|
|
230
|
+
if ("\r".encode(:universal_newline => true) rescue nil) == LF &&
|
|
231
|
+
(LF.encode(:crlf_newline => true) rescue nil) == CRLF
|
|
232
|
+
# Using String#encode is better performing than Regexp
|
|
233
|
+
|
|
234
|
+
def self.to_lf(input)
|
|
235
|
+
input.kind_of?(String) ? input.to_str.encode(input.encoding, :universal_newline => true) : ''
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def self.to_crlf(input)
|
|
239
|
+
input.kind_of?(String) ? input.to_str.encode(input.encoding, :universal_newline => true).encode!(input.encoding, :crlf_newline => true) : ''
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
else
|
|
243
|
+
|
|
244
|
+
def self.to_lf(input)
|
|
245
|
+
input.kind_of?(String) ? input.to_str.gsub(/\r\n|\r/, LF) : ''
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
if RUBY_VERSION >= '1.9'
|
|
249
|
+
# This 1.9 only regex can save a reasonable amount of time (~20%)
|
|
250
|
+
# by not matching "\r\n" so the string is returned unchanged in
|
|
251
|
+
# the common case.
|
|
252
|
+
CRLF_REGEX = Regexp.new("(?<!\r)\n|\r(?!\n)")
|
|
253
|
+
else
|
|
254
|
+
CRLF_REGEX = /\n|\r\n|\r/
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def self.to_crlf(input)
|
|
258
|
+
input.kind_of?(String) ? input.to_str.gsub(CRLF_REGEX, CRLF) : ''
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
# Returns true if the object is considered blank.
|
|
264
|
+
# A blank includes things like '', ' ', nil,
|
|
265
|
+
# and arrays and hashes that have nothing in them.
|
|
266
|
+
#
|
|
267
|
+
# This logic is mostly shared with ActiveSupport's blank?
|
|
268
|
+
def self.blank?(value)
|
|
269
|
+
if value.kind_of?(NilClass)
|
|
270
|
+
true
|
|
271
|
+
elsif value.kind_of?(String)
|
|
272
|
+
value !~ /\S/
|
|
273
|
+
else
|
|
274
|
+
value.respond_to?(:empty?) ? value.empty? : !value
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
|
|
214
278
|
end
|
|
215
279
|
end
|
|
Binary file
|
data/lib/mail/version.rb
CHANGED
|
@@ -1,24 +1,17 @@
|
|
|
1
|
-
#
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
module Mail
|
|
3
3
|
module VERSION
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
version[type] = value
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
MAJOR = version['major']
|
|
13
|
-
MINOR = version['minor']
|
|
14
|
-
PATCH = version['patch']
|
|
15
|
-
BUILD = version['build']
|
|
4
|
+
|
|
5
|
+
MAJOR = 2
|
|
6
|
+
MINOR = 6
|
|
7
|
+
PATCH = 4
|
|
8
|
+
BUILD = nil
|
|
16
9
|
|
|
17
10
|
STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join('.')
|
|
18
|
-
|
|
11
|
+
|
|
19
12
|
def self.version
|
|
20
13
|
STRING
|
|
21
14
|
end
|
|
22
|
-
|
|
15
|
+
|
|
23
16
|
end
|
|
24
17
|
end
|