mail 2.5.3 → 2.6.0
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 +70 -0
- data/CONTRIBUTING.md +17 -4
- data/Dependencies.txt +0 -1
- data/Gemfile +10 -21
- data/MIT-LICENSE +20 -0
- data/README.md +50 -29
- data/Rakefile +11 -20
- data/VERSION +4 -0
- data/lib/mail/attachments_list.rb +2 -2
- data/lib/mail/body.rb +5 -5
- data/lib/mail/check_delivery_params.rb +12 -22
- 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 +16 -6
- data/lib/mail/elements/address.rb +43 -79
- data/lib/mail/elements/address_list.rb +19 -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/encodings/quoted_printable.rb +4 -3
- data/lib/mail/encodings.rb +60 -28
- data/lib/mail/envelope.rb +0 -5
- data/lib/mail/field.rb +63 -28
- 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 +26 -21
- data/lib/mail/fields/common/common_date.rb +0 -7
- data/lib/mail/fields/common/common_field.rb +5 -5
- 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 +1 -1
- data/lib/mail/fields/date_field.rb +1 -1
- data/lib/mail/fields/from_field.rb +2 -2
- data/lib/mail/fields/in_reply_to_field.rb +2 -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 +8 -2
- data/lib/mail/header.rb +13 -23
- data/lib/mail/mail.rb +12 -0
- data/lib/mail/matchers/has_sent_mail.rb +34 -1
- data/lib/mail/message.rb +151 -48
- data/lib/mail/multibyte/chars.rb +2 -2
- data/lib/mail/multibyte/unicode.rb +6 -4
- data/lib/mail/network/delivery_methods/exim.rb +1 -6
- data/lib/mail/network/delivery_methods/file_delivery.rb +1 -1
- data/lib/mail/network/delivery_methods/sendmail.rb +32 -10
- data/lib/mail/network/delivery_methods/smtp.rb +31 -34
- data/lib/mail/network/delivery_methods/smtp_connection.rb +6 -6
- data/lib/mail/network/delivery_methods/test_mailer.rb +2 -2
- data/lib/mail/network/retriever_methods/imap.rb +18 -13
- 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 +64 -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 +184 -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 +2129 -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 +29 -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/patterns.rb +4 -1
- data/lib/mail/utilities.rb +28 -18
- data/lib/mail/version.rb +1 -1
- data/lib/mail/version_specific/ruby_1_8.rb +6 -2
- data/lib/mail/version_specific/ruby_1_9.rb +61 -21
- data/lib/mail.rb +1 -2
- metadata +112 -73
- data/lib/VERSION +0 -4
- data/lib/load_parsers.rb +0 -35
- 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,29 @@
|
|
|
1
|
+
module Mail
|
|
2
|
+
module Parsers
|
|
3
|
+
module Ragel
|
|
4
|
+
module Ruby
|
|
5
|
+
Mail::Parsers::Ragel::FIELD_PARSERS.each do |field_parser|
|
|
6
|
+
require "mail/parsers/ragel/ruby/machines/#{field_parser}_machine"
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
MACHINE_LIST = {
|
|
10
|
+
:address_lists => AddressListsMachine,
|
|
11
|
+
:phrase_lists => PhraseListsMachine,
|
|
12
|
+
:date_time => DateTimeMachine,
|
|
13
|
+
:received => ReceivedMachine,
|
|
14
|
+
:message_ids => MessageIdsMachine,
|
|
15
|
+
:envelope_from => EnvelopeFromMachine,
|
|
16
|
+
:mime_version => MimeVersionMachine,
|
|
17
|
+
:content_type => ContentTypeMachine,
|
|
18
|
+
:content_disposition => ContentDispositionMachine,
|
|
19
|
+
:content_transfer_encoding => ContentTransferEncodingMachine,
|
|
20
|
+
:content_location => ContentLocationMachine
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
def self.parse(machine, string)
|
|
24
|
+
MACHINE_LIST[machine].parse(string)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
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/patterns.rb
CHANGED
|
@@ -20,9 +20,12 @@ module Mail
|
|
|
20
20
|
FWS = /#{CRLF}#{WSP}*/
|
|
21
21
|
TEXT = /[#{text}]/ # + obs-text
|
|
22
22
|
FIELD_NAME = /[#{field_name}]+/
|
|
23
|
-
|
|
23
|
+
FIELD_PREFIX = /\A(#{FIELD_NAME})/
|
|
24
|
+
FIELD_BODY = /.+/m
|
|
24
25
|
FIELD_LINE = /^[#{field_name}]+:\s*.+$/
|
|
26
|
+
FIELD_SPLIT = /^(#{FIELD_NAME})\s*:\s*(#{FIELD_BODY})?$/
|
|
25
27
|
HEADER_LINE = /^([#{field_name}]+:\s*.+)$/
|
|
28
|
+
HEADER_SPLIT = /#{CRLF}(?!#{WSP})/
|
|
26
29
|
|
|
27
30
|
QP_UNSAFE = /[^#{qp_safe}]/
|
|
28
31
|
QP_SAFE = /[#{qp_safe}]/
|
data/lib/mail/utilities.rb
CHANGED
|
@@ -21,7 +21,9 @@ module Mail
|
|
|
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
|
|
@@ -41,28 +43,36 @@ module Mail
|
|
|
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
|
-
str = match[1] if match
|
|
51
|
-
# First remove all escaped double quotes:
|
|
52
|
-
str = str.gsub(/\\"/, '"')
|
|
53
|
-
# Then wrap and re-escape all double quotes
|
|
54
|
-
'"' + str.gsub(/["]/n) {|s| '\\' + s } + '"'
|
|
57
|
+
'"' + unquote(str).gsub(/[\\"]/n) {|s| '\\' + s } + '"'
|
|
55
58
|
end
|
|
56
|
-
|
|
57
|
-
# Unwraps supplied string from inside double quotes
|
|
58
|
-
#
|
|
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.
|
|
@@ -135,7 +145,7 @@ module Mail
|
|
|
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.
|
data/lib/mail/version.rb
CHANGED
|
@@ -3,7 +3,7 @@ module Mail
|
|
|
3
3
|
module VERSION
|
|
4
4
|
|
|
5
5
|
version = {}
|
|
6
|
-
File.read(File.join(File.dirname(__FILE__), '../', 'VERSION')).each_line do |line|
|
|
6
|
+
File.read(File.join(File.dirname(__FILE__), '../', '../', 'VERSION')).each_line do |line|
|
|
7
7
|
type, value = line.chomp.split(":")
|
|
8
8
|
next if type =~ /^\s+$/ || value =~ /^\s+$/
|
|
9
9
|
version[type] = value
|
|
@@ -58,7 +58,7 @@ module Mail
|
|
|
58
58
|
# Ruby 1.8 requires an encoding to work
|
|
59
59
|
raise ArgumentError, "Must supply an encoding" if encoding.nil?
|
|
60
60
|
encoding = encoding.to_s.upcase.gsub('_', '-')
|
|
61
|
-
[Encodings::Base64.encode(str), encoding]
|
|
61
|
+
[Encodings::Base64.encode(str), fix_encoding(encoding)]
|
|
62
62
|
end
|
|
63
63
|
|
|
64
64
|
def Ruby18.b_value_decode(str)
|
|
@@ -105,8 +105,12 @@ module Mail
|
|
|
105
105
|
|
|
106
106
|
def Ruby18.fix_encoding(encoding)
|
|
107
107
|
case encoding.upcase
|
|
108
|
-
when 'UTF8'
|
|
108
|
+
when 'UTF8', 'UTF_8'
|
|
109
109
|
'UTF-8'
|
|
110
|
+
when 'UTF16', 'UTF-16'
|
|
111
|
+
'UTF-16BE'
|
|
112
|
+
when 'UTF32', 'UTF-32'
|
|
113
|
+
'UTF-32BE'
|
|
110
114
|
else
|
|
111
115
|
encoding
|
|
112
116
|
end
|
|
@@ -51,9 +51,9 @@ module Mail
|
|
|
51
51
|
def Ruby19.b_value_decode(str)
|
|
52
52
|
match = str.match(/\=\?(.+)?\?[Bb]\?(.+)?\?\=/m)
|
|
53
53
|
if match
|
|
54
|
-
|
|
54
|
+
charset = match[1]
|
|
55
55
|
str = Ruby19.decode_base64(match[2])
|
|
56
|
-
str.force_encoding(
|
|
56
|
+
str.force_encoding(pick_encoding(charset))
|
|
57
57
|
end
|
|
58
58
|
decoded = str.encode("utf-8", :invalid => :replace, :replace => "")
|
|
59
59
|
decoded.valid_encoding? ? decoded : decoded.encode("utf-16le", :invalid => :replace, :replace => "").encode("utf-8")
|
|
@@ -67,12 +67,15 @@ module Mail
|
|
|
67
67
|
def Ruby19.q_value_decode(str)
|
|
68
68
|
match = str.match(/\=\?(.+)?\?[Qq]\?(.+)?\?\=/m)
|
|
69
69
|
if match
|
|
70
|
-
|
|
70
|
+
charset = match[1]
|
|
71
71
|
string = match[2].gsub(/_/, '=20')
|
|
72
72
|
# Remove trailing = if it exists in a Q encoding
|
|
73
73
|
string = string.sub(/\=$/, '')
|
|
74
74
|
str = Encodings::QuotedPrintable.decode(string)
|
|
75
|
-
str.force_encoding(
|
|
75
|
+
str.force_encoding(pick_encoding(charset))
|
|
76
|
+
# We assume that binary strings hold utf-8 directly to work around
|
|
77
|
+
# jruby/jruby#829 which subtly changes String#encode semantics.
|
|
78
|
+
str.force_encoding('utf-8') if str.encoding == Encoding::ASCII_8BIT
|
|
76
79
|
end
|
|
77
80
|
decoded = str.encode("utf-8", :invalid => :replace, :replace => "")
|
|
78
81
|
decoded.valid_encoding? ? decoded : decoded.encode("utf-16le", :invalid => :replace, :replace => "").encode("utf-8")
|
|
@@ -96,23 +99,60 @@ module Mail
|
|
|
96
99
|
@uri_parser ||= URI::Parser.new
|
|
97
100
|
end
|
|
98
101
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
102
|
+
# Pick a Ruby encoding corresponding to the message charset. Most
|
|
103
|
+
# charsets have a Ruby encoding, but some need manual aliasing here.
|
|
104
|
+
#
|
|
105
|
+
# TODO: add this as a test somewhere:
|
|
106
|
+
# Encoding.list.map { |e| [e.to_s.upcase == pick_encoding(e.to_s.downcase.gsub("-", "")), e.to_s] }.select {|a,b| !b}
|
|
107
|
+
# Encoding.list.map { |e| [e.to_s == pick_encoding(e.to_s), e.to_s] }.select {|a,b| !b}
|
|
108
|
+
def Ruby19.pick_encoding(charset)
|
|
109
|
+
case charset
|
|
110
|
+
|
|
111
|
+
# ISO-8859-8-I etc. http://en.wikipedia.org/wiki/ISO-8859-8-I
|
|
112
|
+
when /^iso-?8859-(\d+)(-i)?$/i
|
|
113
|
+
"ISO-8859-#{$1}"
|
|
114
|
+
|
|
115
|
+
# ISO-8859-15, ISO-2022-JP and alike
|
|
116
|
+
when /iso-?(\d{4})-?(\w{1,2})/i
|
|
117
|
+
"ISO-#{$1}-#{$2}"
|
|
118
|
+
|
|
119
|
+
# "ISO-2022-JP-KDDI" and alike
|
|
120
|
+
when /iso-?(\d{4})-?(\w{1,2})-?(\w*)/i
|
|
121
|
+
"ISO-#{$1}-#{$2}-#{$3}"
|
|
122
|
+
|
|
123
|
+
# UTF-8, UTF-32BE and alike
|
|
124
|
+
when /utf[\-_]?(\d{1,2})?(\w{1,2})/i
|
|
125
|
+
"UTF-#{$1}#{$2}".gsub(/\A(UTF-(?:16|32))\z/, '\\1BE')
|
|
126
|
+
|
|
127
|
+
# Windows-1252 and alike
|
|
128
|
+
when /Windows-?(.*)/i
|
|
129
|
+
"Windows-#{$1}"
|
|
130
|
+
|
|
131
|
+
when /^8bit$/
|
|
132
|
+
Encoding::ASCII_8BIT
|
|
133
|
+
|
|
134
|
+
# alternatives/misspellings of us-ascii seen in the wild
|
|
135
|
+
when /^iso-?646(-us)?$/i, /us=ascii/i
|
|
136
|
+
Encoding::ASCII
|
|
137
|
+
|
|
138
|
+
# Microsoft-specific alias for MACROMAN
|
|
139
|
+
when /^macintosh$/i
|
|
140
|
+
Encoding::MACROMAN
|
|
141
|
+
|
|
142
|
+
# Microsoft-specific alias for CP949 (Korean)
|
|
143
|
+
when 'ks_c_5601-1987'
|
|
144
|
+
Encoding::CP949
|
|
145
|
+
|
|
146
|
+
# Wrongly written Shift_JIS (Japanese)
|
|
147
|
+
when 'shift-jis'
|
|
148
|
+
Encoding::Shift_JIS
|
|
149
|
+
|
|
150
|
+
# GB2312 (Chinese charset) is a subset of GB18030 (its replacement)
|
|
151
|
+
when /gb2312/i
|
|
152
|
+
Encoding::GB18030
|
|
153
|
+
|
|
154
|
+
else
|
|
155
|
+
charset
|
|
116
156
|
end
|
|
117
157
|
end
|
|
118
158
|
end
|
data/lib/mail.rb
CHANGED
|
@@ -29,7 +29,6 @@ module Mail # :doc:
|
|
|
29
29
|
require 'mail/core_extensions/nil'
|
|
30
30
|
require 'mail/core_extensions/object'
|
|
31
31
|
require 'mail/core_extensions/string'
|
|
32
|
-
require 'mail/core_extensions/shell_escape'
|
|
33
32
|
require 'mail/core_extensions/smtp' if RUBY_VERSION < '1.9.3'
|
|
34
33
|
require 'mail/indifferent_hash'
|
|
35
34
|
|
|
@@ -77,7 +76,7 @@ module Mail # :doc:
|
|
|
77
76
|
|
|
78
77
|
require 'mail/envelope'
|
|
79
78
|
|
|
80
|
-
require '
|
|
79
|
+
require 'mail/parsers'
|
|
81
80
|
|
|
82
81
|
# Autoload header field elements and transfer encodings.
|
|
83
82
|
require 'mail/elements'
|