mail 2.4.4 → 2.6.3
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 +156 -0
- data/CONTRIBUTING.md +21 -6
- data/Dependencies.txt +0 -1
- data/Gemfile +10 -21
- data/MIT-LICENSE +20 -0
- data/README.md +52 -32
- data/Rakefile +11 -22
- data/lib/mail/attachments_list.rb +2 -2
- data/lib/mail/body.rb +33 -9
- data/lib/mail/check_delivery_params.rb +20 -0
- data/lib/mail/configuration.rb +1 -1
- data/lib/mail/{patterns.rb → constants.rb} +26 -6
- data/lib/mail/core_extensions/nil.rb +4 -2
- data/lib/mail/core_extensions/object.rb +8 -8
- data/lib/mail/core_extensions/smtp.rb +12 -13
- data/lib/mail/core_extensions/string.rb +19 -6
- data/lib/mail/elements/address.rb +82 -141
- data/lib/mail/elements/address_list.rb +9 -42
- data/lib/mail/elements/content_disposition_element.rb +3 -7
- data/lib/mail/elements/content_location_element.rb +2 -6
- data/lib/mail/elements/content_transfer_encoding_element.rb +3 -10
- data/lib/mail/elements/content_type_element.rb +4 -8
- data/lib/mail/elements/date_time_element.rb +3 -7
- data/lib/mail/elements/envelope_from_element.rb +18 -13
- data/lib/mail/elements/message_ids_element.rb +1 -6
- data/lib/mail/elements/mime_version_element.rb +3 -7
- data/lib/mail/elements/phrase_list.rb +2 -7
- data/lib/mail/elements/received_element.rb +3 -7
- data/lib/mail/elements.rb +12 -12
- data/lib/mail/encodings/quoted_printable.rb +4 -3
- data/lib/mail/encodings/unix_to_unix.rb +17 -0
- data/lib/mail/encodings.rb +53 -38
- data/lib/mail/envelope.rb +0 -5
- data/lib/mail/field.rb +131 -118
- data/lib/mail/field_list.rb +18 -18
- data/lib/mail/fields/bcc_field.rb +2 -2
- data/lib/mail/fields/cc_field.rb +2 -2
- data/lib/mail/fields/comments_field.rb +1 -1
- data/lib/mail/fields/common/common_address.rb +32 -22
- data/lib/mail/fields/common/common_date.rb +0 -7
- data/lib/mail/fields/common/common_field.rb +10 -3
- data/lib/mail/fields/common/common_message_id.rb +9 -5
- data/lib/mail/fields/common/parameter_hash.rb +3 -3
- data/lib/mail/fields/content_disposition_field.rb +1 -0
- data/lib/mail/fields/content_id_field.rb +1 -2
- data/lib/mail/fields/content_transfer_encoding_field.rb +2 -8
- data/lib/mail/fields/content_type_field.rb +5 -2
- data/lib/mail/fields/date_field.rb +14 -14
- 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 +1 -1
- data/lib/mail/fields/message_id_field.rb +2 -3
- 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_from_field.rb +2 -2
- data/lib/mail/fields/resent_sender_field.rb +3 -3
- data/lib/mail/fields/resent_to_field.rb +2 -2
- data/lib/mail/fields/sender_field.rb +8 -8
- data/lib/mail/fields/to_field.rb +2 -2
- data/lib/mail/fields/unstructured_field.rb +44 -31
- data/lib/mail/fields.rb +32 -32
- data/lib/mail/header.rb +47 -37
- data/lib/mail/mail.rb +15 -1
- data/lib/mail/matchers/has_sent_mail.rb +36 -3
- data/lib/mail/message.rb +164 -57
- data/lib/mail/multibyte/chars.rb +4 -4
- data/lib/mail/multibyte/unicode.rb +9 -1
- data/lib/mail/network/delivery_methods/exim.rb +2 -3
- data/lib/mail/network/delivery_methods/file_delivery.rb +5 -0
- data/lib/mail/network/delivery_methods/sendmail.rb +35 -8
- data/lib/mail/network/delivery_methods/smtp.rb +34 -45
- data/lib/mail/network/delivery_methods/smtp_connection.rb +9 -22
- data/lib/mail/network/delivery_methods/test_mailer.rb +6 -2
- data/lib/mail/network/retriever_methods/imap.rb +32 -19
- data/lib/mail/network/retriever_methods/pop3.rb +2 -2
- data/lib/mail/network/retriever_methods/test_retriever.rb +11 -15
- data/lib/mail/network.rb +9 -9
- data/lib/mail/parsers/address_lists_parser.rb +132 -0
- data/lib/mail/parsers/content_disposition_parser.rb +67 -0
- data/lib/mail/parsers/content_location_parser.rb +35 -0
- data/lib/mail/parsers/content_transfer_encoding_parser.rb +33 -0
- data/lib/mail/parsers/content_type_parser.rb +66 -0
- data/lib/mail/parsers/date_time_parser.rb +36 -0
- data/lib/mail/parsers/envelope_from_parser.rb +45 -0
- data/lib/mail/parsers/message_ids_parser.rb +39 -0
- data/lib/mail/parsers/mime_version_parser.rb +41 -0
- data/lib/mail/parsers/phrase_lists_parser.rb +33 -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 +17 -0
- data/lib/mail/parsers/received_parser.rb +47 -0
- data/lib/mail/parsers.rb +26 -0
- data/lib/mail/part.rb +6 -2
- data/lib/mail/parts_list.rb +5 -3
- data/lib/mail/utilities.rb +70 -60
- data/lib/mail/values/unicode_tables.dat +0 -0
- data/lib/mail/version.rb +7 -15
- data/lib/mail/version_specific/ruby_1_8.rb +25 -4
- data/lib/mail/version_specific/ruby_1_9.rb +110 -30
- data/lib/mail.rb +20 -19
- metadata +129 -73
- data/Gemfile.lock +0 -36
- data/lib/VERSION +0 -4
- 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 -5341
- 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,17 @@
|
|
|
1
|
+
module Mail
|
|
2
|
+
module Parsers
|
|
3
|
+
module Ragel
|
|
4
|
+
require 'mail/parsers/ragel/parser_info'
|
|
5
|
+
require "mail/parsers/ragel/ruby"
|
|
6
|
+
|
|
7
|
+
def self.parse(machine, string)
|
|
8
|
+
@machine_module ||= Ruby
|
|
9
|
+
@machine_module.parse(machine, string)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.machine_module=(m)
|
|
13
|
+
@machine_module = m
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module Mail::Parsers
|
|
2
|
+
class ReceivedParser
|
|
3
|
+
|
|
4
|
+
def parse(s)
|
|
5
|
+
actions, error = Ragel.parse(:received, s)
|
|
6
|
+
if error
|
|
7
|
+
raise Mail::Field::ParseError.new(Mail::ReceivedElement, s, error)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
received = ReceivedStruct.new
|
|
11
|
+
|
|
12
|
+
received_tokens_s = date_s = time_s = nil
|
|
13
|
+
actions.each_slice(2) do |action_id, p|
|
|
14
|
+
action = Mail::Parsers::Ragel::ACTIONS[action_id]
|
|
15
|
+
case action
|
|
16
|
+
|
|
17
|
+
# Received Tokens:
|
|
18
|
+
when :received_tokens_s then received_tokens_s = p
|
|
19
|
+
when :received_tokens_e
|
|
20
|
+
received.info = s[received_tokens_s..(p-1)]
|
|
21
|
+
|
|
22
|
+
# Date
|
|
23
|
+
when :date_s then date_s = p
|
|
24
|
+
when :date_e
|
|
25
|
+
received.date = s[date_s..(p-1)].strip
|
|
26
|
+
|
|
27
|
+
# Time
|
|
28
|
+
when :time_s then time_s = p
|
|
29
|
+
when :time_e
|
|
30
|
+
received.time = s[time_s..(p-1)]
|
|
31
|
+
|
|
32
|
+
when :angle_addr_s, :comment_e, :comment_s,
|
|
33
|
+
:domain_e, :domain_s, :local_dot_atom_e,
|
|
34
|
+
:local_dot_atom_pre_comment_e,
|
|
35
|
+
:local_dot_atom_pre_comment_s,
|
|
36
|
+
:local_dot_atom_s, :qstr_e, :qstr_s,
|
|
37
|
+
:local_quoted_string_s, :local_quoted_string_e
|
|
38
|
+
# ignored actions
|
|
39
|
+
|
|
40
|
+
else
|
|
41
|
+
raise Mail::Field::ParseError.new(Mail::ReceivedElement, s, "Failed to process unknown action: #{action}")
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
received
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
data/lib/mail/parsers.rb
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module Mail
|
|
2
|
+
module Parsers
|
|
3
|
+
|
|
4
|
+
# Low-level ragel based parsers
|
|
5
|
+
require 'mail/parsers/ragel'
|
|
6
|
+
|
|
7
|
+
AddressListStruct = Struct.new(:addresses, :group_names, :error)
|
|
8
|
+
AddressStruct = Struct.new(:raw, :domain, :comments, :local,
|
|
9
|
+
:obs_domain_list, :display_name, :group, :error)
|
|
10
|
+
ContentDispositionStruct = Struct.new(:disposition_type, :parameters, :error)
|
|
11
|
+
ContentLocationStruct = Struct.new(:location, :error)
|
|
12
|
+
ContentTransferEncodingStruct = Struct.new(:encoding, :error)
|
|
13
|
+
ContentTypeStruct = Struct.new(:main_type, :sub_type, :parameters, :error)
|
|
14
|
+
DateTimeStruct = Struct.new(:date_string, :time_string, :error)
|
|
15
|
+
EnvelopeFromStruct = Struct.new(:address, :ctime_date, :error)
|
|
16
|
+
MessageIdsStruct = Struct.new(:message_ids, :error)
|
|
17
|
+
MimeVersionStruct = Struct.new(:major, :minor, :error)
|
|
18
|
+
PhraseListsStruct = Struct.new(:phrases, :error)
|
|
19
|
+
ReceivedStruct = Struct.new(:date, :time, :info, :error)
|
|
20
|
+
|
|
21
|
+
require 'mail/parsers/ragel/parser_info'
|
|
22
|
+
Ragel::FIELD_PARSERS.each do |field_parser|
|
|
23
|
+
require "mail/parsers/#{field_parser}_parser"
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
data/lib/mail/part.rb
CHANGED
|
@@ -38,8 +38,12 @@ module Mail
|
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
def add_required_fields
|
|
41
|
-
add_content_id unless has_content_id?
|
|
42
41
|
super
|
|
42
|
+
add_content_id if !has_content_id? && inline?
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def add_required_message_fields
|
|
46
|
+
# Override so we don't add Date, MIME-Version, or Message-ID.
|
|
43
47
|
end
|
|
44
48
|
|
|
45
49
|
def delivery_status_report_part?
|
|
@@ -113,4 +117,4 @@ module Mail
|
|
|
113
117
|
|
|
114
118
|
end
|
|
115
119
|
|
|
116
|
-
end
|
|
120
|
+
end
|
data/lib/mail/parts_list.rb
CHANGED
|
@@ -31,11 +31,13 @@ module Mail
|
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
def sort!(order)
|
|
34
|
-
|
|
34
|
+
# stable sort should be used to maintain the relative order as the parts are added
|
|
35
|
+
i = 0;
|
|
36
|
+
sorted = self.sort_by do |a|
|
|
35
37
|
# OK, 10000 is arbitrary... if anyone actually wants to explicitly sort 10000 parts of a
|
|
36
38
|
# single email message... please show me a use case and I'll put more work into this method,
|
|
37
39
|
# in the meantime, it works :)
|
|
38
|
-
get_order_value(a, order)
|
|
40
|
+
[get_order_value(a, order), i += 1]
|
|
39
41
|
end
|
|
40
42
|
self.clear
|
|
41
43
|
sorted.each { |p| self << p }
|
|
@@ -44,7 +46,7 @@ module Mail
|
|
|
44
46
|
private
|
|
45
47
|
|
|
46
48
|
def get_order_value(part, order)
|
|
47
|
-
if part.respond_to?(:content_type)
|
|
49
|
+
if part.respond_to?(:content_type) && !part[:content_type].nil?
|
|
48
50
|
order.index(part[:content_type].string.downcase) || 10000
|
|
49
51
|
else
|
|
50
52
|
10000
|
data/lib/mail/utilities.rb
CHANGED
|
@@ -1,27 +1,29 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
2
|
module Mail
|
|
3
3
|
module Utilities
|
|
4
|
-
include
|
|
5
|
-
|
|
4
|
+
include Constants
|
|
5
|
+
|
|
6
6
|
# Returns true if the string supplied is free from characters not allowed as an ATOM
|
|
7
7
|
def atom_safe?( str )
|
|
8
8
|
not ATOM_UNSAFE === str
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
# If the string supplied has ATOM unsafe characters in it, will return the string quoted
|
|
11
|
+
# If the string supplied has ATOM unsafe characters in it, will return the string quoted
|
|
12
12
|
# in double quotes, otherwise returns the string unmodified
|
|
13
13
|
def quote_atom( str )
|
|
14
14
|
atom_safe?( str ) ? str : dquote(str)
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
# If the string supplied has PHRASE unsafe characters in it, will return the string quoted
|
|
17
|
+
# If the string supplied has PHRASE unsafe characters in it, will return the string quoted
|
|
18
18
|
# in double quotes, otherwise returns the string unmodified
|
|
19
19
|
def quote_phrase( str )
|
|
20
20
|
if RUBY_VERSION >= '1.9'
|
|
21
21
|
original_encoding = str.encoding
|
|
22
22
|
str.force_encoding('ASCII-8BIT')
|
|
23
23
|
if (PHRASE_UNSAFE === str)
|
|
24
|
-
dquote(str).force_encoding(original_encoding)
|
|
24
|
+
quoted_str = dquote(str).force_encoding(original_encoding)
|
|
25
|
+
str.force_encoding(original_encoding)
|
|
26
|
+
quoted_str
|
|
25
27
|
else
|
|
26
28
|
str.force_encoding(original_encoding)
|
|
27
29
|
end
|
|
@@ -35,150 +37,158 @@ module Mail
|
|
|
35
37
|
not TOKEN_UNSAFE === str
|
|
36
38
|
end
|
|
37
39
|
|
|
38
|
-
# If the string supplied has TOKEN unsafe characters in it, will return the string quoted
|
|
40
|
+
# If the string supplied has TOKEN unsafe characters in it, will return the string quoted
|
|
39
41
|
# in double quotes, otherwise returns the string unmodified
|
|
40
42
|
def quote_token( str )
|
|
41
43
|
token_safe?( str ) ? str : dquote(str)
|
|
42
44
|
end
|
|
43
45
|
|
|
44
|
-
# Wraps supplied string in double quotes
|
|
45
|
-
#
|
|
46
|
-
#
|
|
47
|
-
#
|
|
46
|
+
# Wraps supplied string in double quotes and applies \-escaping as necessary,
|
|
47
|
+
# unless it is already wrapped.
|
|
48
|
+
#
|
|
49
|
+
# Example:
|
|
50
|
+
#
|
|
51
|
+
# string = 'This is a string'
|
|
52
|
+
# dquote(string) #=> '"This is a string"'
|
|
53
|
+
#
|
|
54
|
+
# string = 'This is "a string"'
|
|
55
|
+
# dquote(string #=> '"This is \"a string\"'
|
|
48
56
|
def dquote( str )
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
# Unwraps supplied string from inside double quotes.
|
|
58
|
-
#
|
|
57
|
+
'"' + unquote(str).gsub(/[\\"]/n) {|s| '\\' + s } + '"'
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Unwraps supplied string from inside double quotes and
|
|
61
|
+
# removes any \-escaping.
|
|
62
|
+
#
|
|
59
63
|
# Example:
|
|
60
|
-
#
|
|
64
|
+
#
|
|
61
65
|
# string = '"This is a string"'
|
|
62
66
|
# unquote(string) #=> 'This is a string'
|
|
67
|
+
#
|
|
68
|
+
# string = '"This is \"a string\""'
|
|
69
|
+
# unqoute(string) #=> 'This is "a string"'
|
|
63
70
|
def unquote( str )
|
|
64
|
-
|
|
65
|
-
|
|
71
|
+
if str =~ /^"(.*?)"$/
|
|
72
|
+
$1.gsub(/\\(.)/, '\1')
|
|
73
|
+
else
|
|
74
|
+
str
|
|
75
|
+
end
|
|
66
76
|
end
|
|
67
|
-
|
|
77
|
+
|
|
68
78
|
# Wraps a string in parenthesis and escapes any that are in the string itself.
|
|
69
|
-
#
|
|
79
|
+
#
|
|
70
80
|
# Example:
|
|
71
|
-
#
|
|
81
|
+
#
|
|
72
82
|
# paren( 'This is a string' ) #=> '(This is a string)'
|
|
73
83
|
def paren( str )
|
|
74
84
|
RubyVer.paren( str )
|
|
75
85
|
end
|
|
76
|
-
|
|
86
|
+
|
|
77
87
|
# Unwraps a string from being wrapped in parenthesis
|
|
78
|
-
#
|
|
88
|
+
#
|
|
79
89
|
# Example:
|
|
80
|
-
#
|
|
90
|
+
#
|
|
81
91
|
# str = '(This is a string)'
|
|
82
92
|
# unparen( str ) #=> 'This is a string'
|
|
83
93
|
def unparen( str )
|
|
84
94
|
match = str.match(/^\((.*?)\)$/)
|
|
85
95
|
match ? match[1] : str
|
|
86
96
|
end
|
|
87
|
-
|
|
97
|
+
|
|
88
98
|
# Wraps a string in angle brackets and escapes any that are in the string itself
|
|
89
|
-
#
|
|
99
|
+
#
|
|
90
100
|
# Example:
|
|
91
|
-
#
|
|
101
|
+
#
|
|
92
102
|
# bracket( 'This is a string' ) #=> '<This is a string>'
|
|
93
103
|
def bracket( str )
|
|
94
104
|
RubyVer.bracket( str )
|
|
95
105
|
end
|
|
96
|
-
|
|
106
|
+
|
|
97
107
|
# Unwraps a string from being wrapped in parenthesis
|
|
98
|
-
#
|
|
108
|
+
#
|
|
99
109
|
# Example:
|
|
100
|
-
#
|
|
110
|
+
#
|
|
101
111
|
# str = '<This is a string>'
|
|
102
112
|
# unbracket( str ) #=> 'This is a string'
|
|
103
113
|
def unbracket( str )
|
|
104
114
|
match = str.match(/^\<(.*?)\>$/)
|
|
105
115
|
match ? match[1] : str
|
|
106
116
|
end
|
|
107
|
-
|
|
117
|
+
|
|
108
118
|
# Escape parenthesies in a string
|
|
109
|
-
#
|
|
119
|
+
#
|
|
110
120
|
# Example:
|
|
111
|
-
#
|
|
121
|
+
#
|
|
112
122
|
# str = 'This is (a) string'
|
|
113
123
|
# escape_paren( str ) #=> 'This is \(a\) string'
|
|
114
124
|
def escape_paren( str )
|
|
115
125
|
RubyVer.escape_paren( str )
|
|
116
126
|
end
|
|
117
|
-
|
|
127
|
+
|
|
118
128
|
def uri_escape( str )
|
|
119
129
|
uri_parser.escape(str)
|
|
120
130
|
end
|
|
121
|
-
|
|
131
|
+
|
|
122
132
|
def uri_unescape( str )
|
|
123
133
|
uri_parser.unescape(str)
|
|
124
134
|
end
|
|
125
|
-
|
|
135
|
+
|
|
126
136
|
def uri_parser
|
|
127
137
|
@uri_parser ||= URI.const_defined?(:Parser) ? URI::Parser.new : URI
|
|
128
138
|
end
|
|
129
|
-
|
|
139
|
+
|
|
130
140
|
# Matches two objects with their to_s values case insensitively
|
|
131
|
-
#
|
|
141
|
+
#
|
|
132
142
|
# Example:
|
|
133
|
-
#
|
|
143
|
+
#
|
|
134
144
|
# obj2 = "This_is_An_object"
|
|
135
145
|
# obj1 = :this_IS_an_object
|
|
136
146
|
# match_to_s( obj1, obj2 ) #=> true
|
|
137
147
|
def match_to_s( obj1, obj2 )
|
|
138
|
-
obj1.to_s.
|
|
148
|
+
obj1.to_s.casecmp(obj2.to_s) == 0
|
|
139
149
|
end
|
|
140
|
-
|
|
150
|
+
|
|
141
151
|
# Capitalizes a string that is joined by hyphens correctly.
|
|
142
|
-
#
|
|
152
|
+
#
|
|
143
153
|
# Example:
|
|
144
|
-
#
|
|
154
|
+
#
|
|
145
155
|
# string = 'resent-from-field'
|
|
146
156
|
# capitalize_field( string ) #=> 'Resent-From-Field'
|
|
147
157
|
def capitalize_field( str )
|
|
148
158
|
str.to_s.split("-").map { |v| v.capitalize }.join("-")
|
|
149
159
|
end
|
|
150
|
-
|
|
160
|
+
|
|
151
161
|
# Takes an underscored word and turns it into a class name
|
|
152
|
-
#
|
|
162
|
+
#
|
|
153
163
|
# Example:
|
|
154
|
-
#
|
|
164
|
+
#
|
|
155
165
|
# constantize("hello") #=> "Hello"
|
|
156
166
|
# constantize("hello-there") #=> "HelloThere"
|
|
157
167
|
# constantize("hello-there-mate") #=> "HelloThereMate"
|
|
158
168
|
def constantize( str )
|
|
159
169
|
str.to_s.split(/[-_]/).map { |v| v.capitalize }.to_s
|
|
160
170
|
end
|
|
161
|
-
|
|
171
|
+
|
|
162
172
|
# Swaps out all underscores (_) for hyphens (-) good for stringing from symbols
|
|
163
173
|
# a field name.
|
|
164
|
-
#
|
|
174
|
+
#
|
|
165
175
|
# Example:
|
|
166
|
-
#
|
|
176
|
+
#
|
|
167
177
|
# string = :resent_from_field
|
|
168
178
|
# dasherize ( string ) #=> 'resent_from_field'
|
|
169
179
|
def dasherize( str )
|
|
170
|
-
str.to_s.
|
|
180
|
+
str.to_s.tr(UNDERSCORE, HYPHEN)
|
|
171
181
|
end
|
|
172
182
|
|
|
173
183
|
# Swaps out all hyphens (-) for underscores (_) good for stringing to symbols
|
|
174
184
|
# a field name.
|
|
175
|
-
#
|
|
185
|
+
#
|
|
176
186
|
# Example:
|
|
177
|
-
#
|
|
187
|
+
#
|
|
178
188
|
# string = :resent_from_field
|
|
179
189
|
# underscoreize ( string ) #=> 'resent_from_field'
|
|
180
190
|
def underscoreize( str )
|
|
181
|
-
str.to_s.downcase.
|
|
191
|
+
str.to_s.downcase.tr(HYPHEN, UNDERSCORE)
|
|
182
192
|
end
|
|
183
193
|
|
|
184
194
|
if RUBY_VERSION <= '1.8.6'
|
|
@@ -190,7 +200,7 @@ module Mail
|
|
|
190
200
|
end
|
|
191
201
|
results
|
|
192
202
|
end
|
|
193
|
-
|
|
203
|
+
|
|
194
204
|
def map_with_index( enum, &block )
|
|
195
205
|
results = []
|
|
196
206
|
enum.each_with_index do |token, i|
|
|
@@ -198,7 +208,7 @@ module Mail
|
|
|
198
208
|
end
|
|
199
209
|
results
|
|
200
210
|
end
|
|
201
|
-
|
|
211
|
+
|
|
202
212
|
else
|
|
203
213
|
|
|
204
214
|
def map_lines( str, &block )
|
|
Binary file
|
data/lib/mail/version.rb
CHANGED
|
@@ -1,24 +1,16 @@
|
|
|
1
|
-
# encoding: utf-8
|
|
2
1
|
module Mail
|
|
3
2
|
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']
|
|
3
|
+
|
|
4
|
+
MAJOR = 2
|
|
5
|
+
MINOR = 6
|
|
6
|
+
PATCH = 3
|
|
7
|
+
BUILD = nil
|
|
16
8
|
|
|
17
9
|
STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join('.')
|
|
18
|
-
|
|
10
|
+
|
|
19
11
|
def self.version
|
|
20
12
|
STRING
|
|
21
13
|
end
|
|
22
|
-
|
|
14
|
+
|
|
23
15
|
end
|
|
24
16
|
end
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
module Mail
|
|
4
4
|
class Ruby18
|
|
5
5
|
require 'base64'
|
|
6
|
+
require 'iconv'
|
|
6
7
|
|
|
7
8
|
# Escapes any parenthesis in a string that are unescaped. This can't
|
|
8
9
|
# use the Ruby 1.9.1 regexp feature of negative look behind so we have
|
|
@@ -57,14 +58,15 @@ module Mail
|
|
|
57
58
|
# Ruby 1.8 requires an encoding to work
|
|
58
59
|
raise ArgumentError, "Must supply an encoding" if encoding.nil?
|
|
59
60
|
encoding = encoding.to_s.upcase.gsub('_', '-')
|
|
60
|
-
[Encodings::Base64.encode(str), encoding]
|
|
61
|
+
[Encodings::Base64.encode(str), fix_encoding(encoding)]
|
|
61
62
|
end
|
|
62
63
|
|
|
63
64
|
def Ruby18.b_value_decode(str)
|
|
64
|
-
match = str.match(/\=\?(.+)?\?[Bb]\?(
|
|
65
|
+
match = str.match(/\=\?(.+)?\?[Bb]\?(.*)\?\=/m)
|
|
65
66
|
if match
|
|
66
67
|
encoding = match[1]
|
|
67
68
|
str = Ruby18.decode_base64(match[2])
|
|
69
|
+
str = Iconv.conv('UTF-8//IGNORE', fix_encoding(encoding), str)
|
|
68
70
|
end
|
|
69
71
|
str
|
|
70
72
|
end
|
|
@@ -77,10 +79,14 @@ module Mail
|
|
|
77
79
|
end
|
|
78
80
|
|
|
79
81
|
def Ruby18.q_value_decode(str)
|
|
80
|
-
match = str.match(/\=\?(.+)?\?[Qq]\?(
|
|
82
|
+
match = str.match(/\=\?(.+)?\?[Qq]\?(.*)\?\=/m)
|
|
81
83
|
if match
|
|
82
84
|
encoding = match[1]
|
|
83
|
-
|
|
85
|
+
string = match[2].gsub(/_/, '=20')
|
|
86
|
+
# Remove trailing = if it exists in a Q encoding
|
|
87
|
+
string = string.sub(/\=$/, '')
|
|
88
|
+
str = Encodings::QuotedPrintable.decode(string)
|
|
89
|
+
str = Iconv.conv('UTF-8//IGNORE', fix_encoding(encoding), str)
|
|
84
90
|
end
|
|
85
91
|
str
|
|
86
92
|
end
|
|
@@ -94,5 +100,20 @@ module Mail
|
|
|
94
100
|
language = Configuration.instance.param_encode_language
|
|
95
101
|
"#{encoding}'#{language}'#{URI.escape(str)}"
|
|
96
102
|
end
|
|
103
|
+
|
|
104
|
+
private
|
|
105
|
+
|
|
106
|
+
def Ruby18.fix_encoding(encoding)
|
|
107
|
+
case encoding.upcase
|
|
108
|
+
when 'UTF8', 'UTF_8'
|
|
109
|
+
'UTF-8'
|
|
110
|
+
when 'UTF16', 'UTF-16'
|
|
111
|
+
'UTF-16BE'
|
|
112
|
+
when 'UTF32', 'UTF-32'
|
|
113
|
+
'UTF-32BE'
|
|
114
|
+
else
|
|
115
|
+
encoding
|
|
116
|
+
end
|
|
117
|
+
end
|
|
97
118
|
end
|
|
98
119
|
end
|