mail 2.5.5 → 2.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/MIT-LICENSE +1 -1
- data/README.md +170 -108
- data/lib/mail/attachments_list.rb +13 -10
- data/lib/mail/body.rb +105 -91
- data/lib/mail/check_delivery_params.rb +30 -22
- data/lib/mail/configuration.rb +3 -0
- data/lib/mail/constants.rb +79 -0
- data/lib/mail/elements/address.rb +118 -174
- data/lib/mail/elements/address_list.rb +16 -56
- data/lib/mail/elements/content_disposition_element.rb +12 -22
- data/lib/mail/elements/content_location_element.rb +9 -17
- data/lib/mail/elements/content_transfer_encoding_element.rb +8 -19
- data/lib/mail/elements/content_type_element.rb +20 -30
- data/lib/mail/elements/date_time_element.rb +10 -21
- data/lib/mail/elements/envelope_from_element.rb +23 -31
- data/lib/mail/elements/message_ids_element.rb +22 -20
- data/lib/mail/elements/mime_version_element.rb +10 -21
- data/lib/mail/elements/phrase_list.rb +13 -15
- data/lib/mail/elements/received_element.rb +26 -21
- data/lib/mail/elements.rb +1 -0
- data/lib/mail/encodings/7bit.rb +10 -14
- data/lib/mail/encodings/8bit.rb +5 -18
- data/lib/mail/encodings/base64.rb +15 -10
- data/lib/mail/encodings/binary.rb +4 -22
- data/lib/mail/encodings/identity.rb +24 -0
- data/lib/mail/encodings/quoted_printable.rb +13 -7
- data/lib/mail/encodings/transfer_encoding.rb +47 -28
- data/lib/mail/encodings/unix_to_unix.rb +20 -0
- data/lib/mail/encodings.rb +102 -93
- data/lib/mail/envelope.rb +12 -19
- data/lib/mail/field.rb +143 -71
- data/lib/mail/field_list.rb +73 -19
- data/lib/mail/fields/bcc_field.rb +42 -48
- data/lib/mail/fields/cc_field.rb +29 -50
- data/lib/mail/fields/comments_field.rb +28 -37
- data/lib/mail/fields/common_address_field.rb +170 -0
- data/lib/mail/fields/common_date_field.rb +58 -0
- data/lib/mail/fields/common_field.rb +77 -0
- data/lib/mail/fields/common_message_id_field.rb +42 -0
- data/lib/mail/fields/content_description_field.rb +8 -14
- data/lib/mail/fields/content_disposition_field.rb +20 -44
- data/lib/mail/fields/content_id_field.rb +25 -51
- data/lib/mail/fields/content_location_field.rb +12 -25
- data/lib/mail/fields/content_transfer_encoding_field.rb +31 -36
- data/lib/mail/fields/content_type_field.rb +51 -80
- data/lib/mail/fields/date_field.rb +24 -52
- data/lib/mail/fields/from_field.rb +29 -50
- data/lib/mail/fields/in_reply_to_field.rb +39 -49
- data/lib/mail/fields/keywords_field.rb +19 -32
- data/lib/mail/fields/message_id_field.rb +26 -71
- data/lib/mail/fields/mime_version_field.rb +20 -30
- data/lib/mail/fields/named_structured_field.rb +11 -0
- data/lib/mail/fields/named_unstructured_field.rb +11 -0
- data/lib/mail/fields/optional_field.rb +10 -7
- data/lib/mail/fields/{common/parameter_hash.rb → parameter_hash.rb} +16 -13
- data/lib/mail/fields/received_field.rb +44 -57
- data/lib/mail/fields/references_field.rb +36 -49
- data/lib/mail/fields/reply_to_field.rb +29 -50
- data/lib/mail/fields/resent_bcc_field.rb +29 -50
- data/lib/mail/fields/resent_cc_field.rb +29 -50
- data/lib/mail/fields/resent_date_field.rb +6 -30
- data/lib/mail/fields/resent_from_field.rb +29 -50
- data/lib/mail/fields/resent_message_id_field.rb +6 -29
- data/lib/mail/fields/resent_sender_field.rb +28 -57
- data/lib/mail/fields/resent_to_field.rb +29 -50
- data/lib/mail/fields/return_path_field.rb +51 -55
- data/lib/mail/fields/sender_field.rb +35 -56
- data/lib/mail/fields/structured_field.rb +4 -30
- data/lib/mail/fields/subject_field.rb +10 -11
- data/lib/mail/fields/to_field.rb +29 -50
- data/lib/mail/fields/unstructured_field.rb +43 -51
- data/lib/mail/fields.rb +1 -0
- data/lib/mail/header.rb +78 -129
- data/lib/mail/indifferent_hash.rb +1 -0
- data/lib/mail/mail.rb +18 -11
- data/lib/mail/matchers/attachment_matchers.rb +44 -0
- data/lib/mail/matchers/has_sent_mail.rb +81 -4
- data/lib/mail/message.rb +142 -139
- data/lib/mail/multibyte/chars.rb +24 -180
- data/lib/mail/multibyte/unicode.rb +32 -27
- data/lib/mail/multibyte/utils.rb +27 -43
- data/lib/mail/multibyte.rb +56 -16
- data/lib/mail/network/delivery_methods/exim.rb +6 -4
- data/lib/mail/network/delivery_methods/file_delivery.rb +12 -10
- data/lib/mail/network/delivery_methods/logger_delivery.rb +34 -0
- data/lib/mail/network/delivery_methods/sendmail.rb +63 -21
- data/lib/mail/network/delivery_methods/smtp.rb +76 -50
- data/lib/mail/network/delivery_methods/smtp_connection.rb +4 -4
- data/lib/mail/network/delivery_methods/test_mailer.rb +5 -2
- data/lib/mail/network/retriever_methods/base.rb +9 -8
- data/lib/mail/network/retriever_methods/imap.rb +37 -18
- data/lib/mail/network/retriever_methods/pop3.rb +6 -3
- data/lib/mail/network/retriever_methods/test_retriever.rb +4 -2
- data/lib/mail/network.rb +2 -0
- data/lib/mail/parser_tools.rb +15 -0
- data/lib/mail/parsers/address_lists_parser.rb +33242 -0
- data/lib/mail/parsers/address_lists_parser.rl +179 -0
- data/lib/mail/parsers/content_disposition_parser.rb +901 -0
- data/lib/mail/parsers/content_disposition_parser.rl +89 -0
- data/lib/mail/parsers/content_location_parser.rb +822 -0
- data/lib/mail/parsers/content_location_parser.rl +78 -0
- data/lib/mail/parsers/content_transfer_encoding_parser.rb +522 -0
- data/lib/mail/parsers/content_transfer_encoding_parser.rl +71 -0
- data/lib/mail/parsers/content_type_parser.rb +1048 -0
- data/lib/mail/parsers/content_type_parser.rl +90 -0
- data/lib/mail/parsers/date_time_parser.rb +891 -0
- data/lib/mail/parsers/date_time_parser.rl +69 -0
- data/lib/mail/parsers/envelope_from_parser.rb +3675 -0
- data/lib/mail/parsers/envelope_from_parser.rl +89 -0
- data/lib/mail/parsers/message_ids_parser.rb +5161 -0
- data/lib/mail/parsers/message_ids_parser.rl +93 -0
- data/lib/mail/parsers/mime_version_parser.rb +513 -0
- data/lib/mail/parsers/mime_version_parser.rl +68 -0
- data/lib/mail/parsers/phrase_lists_parser.rb +884 -0
- data/lib/mail/parsers/phrase_lists_parser.rl +90 -0
- data/lib/mail/parsers/received_parser.rb +8782 -0
- data/lib/mail/parsers/received_parser.rl +91 -0
- data/lib/mail/parsers/rfc2045_content_transfer_encoding.rl +13 -0
- data/lib/mail/parsers/rfc2045_content_type.rl +25 -0
- data/lib/mail/parsers/rfc2045_mime.rl +16 -0
- data/lib/mail/parsers/rfc2183_content_disposition.rl +15 -0
- data/lib/mail/parsers/rfc3629_utf8.rl +19 -0
- data/lib/mail/parsers/rfc5234_abnf_core_rules.rl +22 -0
- data/lib/mail/parsers/rfc5322.rl +74 -0
- data/lib/mail/parsers/rfc5322_address.rl +72 -0
- data/lib/mail/parsers/rfc5322_date_time.rl +37 -0
- data/lib/mail/parsers/rfc5322_lexical_tokens.rl +60 -0
- data/lib/mail/parsers.rb +13 -0
- data/lib/mail/part.rb +11 -12
- data/lib/mail/parts_list.rb +90 -14
- data/lib/mail/smtp_envelope.rb +57 -0
- data/lib/mail/utilities.rb +415 -76
- data/lib/mail/values/unicode_tables.dat +0 -0
- data/lib/mail/version.rb +8 -15
- data/lib/mail/yaml.rb +30 -0
- data/lib/mail.rb +9 -32
- metadata +127 -79
- data/CHANGELOG.rdoc +0 -742
- data/CONTRIBUTING.md +0 -45
- data/Dependencies.txt +0 -3
- data/Gemfile +0 -32
- data/Rakefile +0 -21
- data/TODO.rdoc +0 -9
- data/lib/VERSION +0 -4
- data/lib/load_parsers.rb +0 -35
- data/lib/mail/core_extensions/nil.rb +0 -19
- data/lib/mail/core_extensions/object.rb +0 -13
- data/lib/mail/core_extensions/smtp.rb +0 -24
- data/lib/mail/core_extensions/string/access.rb +0 -145
- data/lib/mail/core_extensions/string/multibyte.rb +0 -78
- data/lib/mail/core_extensions/string.rb +0 -33
- data/lib/mail/fields/common/address_container.rb +0 -16
- data/lib/mail/fields/common/common_address.rb +0 -140
- data/lib/mail/fields/common/common_date.rb +0 -42
- data/lib/mail/fields/common/common_field.rb +0 -57
- data/lib/mail/fields/common/common_message_id.rb +0 -48
- data/lib/mail/multibyte/exceptions.rb +0 -8
- 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 -201
- data/lib/mail/parsers/content_transfer_encoding.treetop +0 -18
- data/lib/mail/parsers/content_type.rb +0 -971
- 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 -421
- data/lib/mail/parsers/rfc2045.treetop +0 -35
- data/lib/mail/parsers/rfc2822.rb +0 -5397
- data/lib/mail/parsers/rfc2822.treetop +0 -408
- data/lib/mail/parsers/rfc2822_obsolete.rb +0 -3768
- data/lib/mail/parsers/rfc2822_obsolete.treetop +0 -241
- data/lib/mail/patterns.rb +0 -35
- data/lib/mail/version_specific/ruby_1_8.rb +0 -119
- data/lib/mail/version_specific/ruby_1_9.rb +0 -147
- data/lib/tasks/corpus.rake +0 -125
- data/lib/tasks/treetop.rake +0 -10
@@ -1,4 +1,5 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
2
3
|
module Mail
|
3
4
|
module Encodings
|
4
5
|
class TransferEncoding
|
@@ -6,52 +7,70 @@ module Mail
|
|
6
7
|
|
7
8
|
PRIORITY = -1
|
8
9
|
|
10
|
+
# And encoding's superclass can always transport it since the
|
11
|
+
# class hierarchy is arranged e.g. Base64 < 7bit < 8bit < Binary.
|
9
12
|
def self.can_transport?(enc)
|
10
|
-
enc
|
11
|
-
if Encodings.defined? enc
|
12
|
-
Encodings.get_encoding(enc).new.is_a? self
|
13
|
-
else
|
14
|
-
false
|
15
|
-
end
|
13
|
+
enc && enc <= self
|
16
14
|
end
|
17
15
|
|
16
|
+
# Override in subclasses to indicate that they can encode text
|
17
|
+
# that couldn't be directly transported, e.g. Base64 has 7bit output,
|
18
|
+
# but it can encode binary.
|
18
19
|
def self.can_encode?(enc)
|
19
|
-
can_transport? enc
|
20
|
+
can_transport? enc
|
20
21
|
end
|
21
22
|
|
22
23
|
def self.cost(str)
|
23
24
|
raise "Unimplemented"
|
24
25
|
end
|
25
26
|
|
27
|
+
def self.compatible_input?(str)
|
28
|
+
true
|
29
|
+
end
|
30
|
+
|
26
31
|
def self.to_s
|
27
32
|
self::NAME
|
28
33
|
end
|
29
34
|
|
30
|
-
def self.
|
31
|
-
|
32
|
-
|
35
|
+
def self.negotiate(message_encoding, source_encoding, str, allowed_encodings = nil)
|
36
|
+
message_encoding = Encodings.get_encoding(message_encoding) || Encodings.get_encoding('8bit')
|
37
|
+
source_encoding = Encodings.get_encoding(source_encoding)
|
38
|
+
|
39
|
+
if message_encoding && source_encoding && message_encoding.can_transport?(source_encoding) && source_encoding.compatible_input?(str)
|
40
|
+
source_encoding
|
33
41
|
else
|
34
|
-
|
35
|
-
Encodings.get_all.each do |enc|
|
36
|
-
choices << enc if self.can_transport? enc and enc.can_encode? source_encoding
|
37
|
-
end
|
38
|
-
best = nil
|
39
|
-
best_cost = 100
|
40
|
-
choices.each do |enc|
|
41
|
-
this_cost = enc.cost str
|
42
|
-
if this_cost < best_cost then
|
43
|
-
best_cost = this_cost
|
44
|
-
best = enc
|
45
|
-
elsif this_cost == best_cost then
|
46
|
-
best = enc if enc::PRIORITY < best::PRIORITY
|
47
|
-
end
|
48
|
-
end
|
49
|
-
best
|
42
|
+
renegotiate(message_encoding, source_encoding, str, allowed_encodings)
|
50
43
|
end
|
51
44
|
end
|
52
45
|
|
53
|
-
def
|
54
|
-
|
46
|
+
def self.renegotiate(message_encoding, source_encoding, str, allowed_encodings = nil)
|
47
|
+
encodings = Encodings.get_all.select do |enc|
|
48
|
+
(allowed_encodings.nil? || allowed_encodings.include?(enc)) &&
|
49
|
+
message_encoding.can_transport?(enc) &&
|
50
|
+
enc.can_encode?(source_encoding)
|
51
|
+
end
|
52
|
+
|
53
|
+
lowest_cost(str, encodings)
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.lowest_cost(str, encodings)
|
57
|
+
best = nil
|
58
|
+
best_cost = nil
|
59
|
+
|
60
|
+
encodings.each do |enc|
|
61
|
+
# If the current choice cannot be transported safely, give priority
|
62
|
+
# to other choices but allow it to be used as a fallback.
|
63
|
+
this_cost = enc.cost(str) if enc.compatible_input?(str)
|
64
|
+
|
65
|
+
if !best_cost || (this_cost && this_cost < best_cost)
|
66
|
+
best_cost = this_cost
|
67
|
+
best = enc
|
68
|
+
elsif this_cost == best_cost
|
69
|
+
best = enc if enc::PRIORITY < best::PRIORITY
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
best
|
55
74
|
end
|
56
75
|
end
|
57
76
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Mail
|
3
|
+
module Encodings
|
4
|
+
class UnixToUnix < TransferEncoding
|
5
|
+
NAME = "x-uuencode"
|
6
|
+
|
7
|
+
def self.decode(str)
|
8
|
+
str.sub(/\Abegin \d+ [^\n]*\n/, '').unpack('u').first
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.encode(str)
|
12
|
+
[str].pack("u")
|
13
|
+
end
|
14
|
+
|
15
|
+
Encodings.register(NAME, self)
|
16
|
+
Encodings.register("uuencode", self)
|
17
|
+
Encodings.register("x-uue", self)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/mail/encodings.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
module Mail
|
4
5
|
# Raised when attempting to decode an unknown encoding type
|
@@ -6,8 +7,7 @@ module Mail
|
|
6
7
|
end
|
7
8
|
|
8
9
|
module Encodings
|
9
|
-
|
10
|
-
include Mail::Patterns
|
10
|
+
include Mail::Constants
|
11
11
|
extend Mail::Utilities
|
12
12
|
|
13
13
|
@transfer_encodings = {}
|
@@ -18,7 +18,7 @@ module Mail
|
|
18
18
|
#
|
19
19
|
# Encodings.register "base64", Mail::Encodings::Base64
|
20
20
|
def Encodings.register(name, cls)
|
21
|
-
|
21
|
+
@transfer_encodings[get_name(name)] = cls
|
22
22
|
end
|
23
23
|
|
24
24
|
# Is the encoding we want defined?
|
@@ -26,8 +26,8 @@ module Mail
|
|
26
26
|
# Example:
|
27
27
|
#
|
28
28
|
# Encodings.defined?(:base64) #=> true
|
29
|
-
def Encodings.defined?(
|
30
|
-
@transfer_encodings.include? get_name(
|
29
|
+
def Encodings.defined?(name)
|
30
|
+
@transfer_encodings.include? get_name(name)
|
31
31
|
end
|
32
32
|
|
33
33
|
# Gets a defined encoding type, QuotedPrintable or Base64 for now.
|
@@ -38,16 +38,24 @@ module Mail
|
|
38
38
|
# Example:
|
39
39
|
#
|
40
40
|
# Encodings.get_encoding(:base64) #=> Mail::Encodings::Base64
|
41
|
-
def Encodings.get_encoding(
|
42
|
-
@transfer_encodings[get_name(
|
41
|
+
def Encodings.get_encoding(name)
|
42
|
+
@transfer_encodings[get_name(name)]
|
43
43
|
end
|
44
44
|
|
45
45
|
def Encodings.get_all
|
46
46
|
@transfer_encodings.values
|
47
47
|
end
|
48
48
|
|
49
|
-
def Encodings.get_name(
|
50
|
-
|
49
|
+
def Encodings.get_name(name)
|
50
|
+
underscoreize(name).downcase
|
51
|
+
end
|
52
|
+
|
53
|
+
def Encodings.transcode_charset(str, from_charset, to_charset = 'UTF-8')
|
54
|
+
if from_charset
|
55
|
+
Utilities.transcode_charset str, from_charset, to_charset
|
56
|
+
else
|
57
|
+
str
|
58
|
+
end
|
51
59
|
end
|
52
60
|
|
53
61
|
# Encodes a parameter value using URI Escaping, note the language field 'en' can
|
@@ -57,8 +65,7 @@ module Mail
|
|
57
65
|
# param_encode_language 'jp'
|
58
66
|
# end
|
59
67
|
#
|
60
|
-
# The character set used for encoding will
|
61
|
-
# Ruby < 1.9 or the encoding on the string passed in.
|
68
|
+
# The character set used for encoding will be the encoding on the string passed in.
|
62
69
|
#
|
63
70
|
# Example:
|
64
71
|
#
|
@@ -70,7 +77,7 @@ module Mail
|
|
70
77
|
when str.ascii_only?
|
71
78
|
str
|
72
79
|
else
|
73
|
-
|
80
|
+
Utilities.param_encode(str)
|
74
81
|
end
|
75
82
|
end
|
76
83
|
|
@@ -84,15 +91,15 @@ module Mail
|
|
84
91
|
# str.encoding #=> 'ISO-8859-1' ## Only on Ruby 1.9
|
85
92
|
# str #=> "This is fun"
|
86
93
|
def Encodings.param_decode(str, encoding)
|
87
|
-
|
94
|
+
Utilities.param_decode(str, encoding)
|
88
95
|
end
|
89
96
|
|
90
97
|
# Decodes or encodes a string as needed for either Base64 or QP encoding types in
|
91
98
|
# the =?<encoding>?[QB]?<string>?=" format.
|
92
99
|
#
|
93
100
|
# The output type needs to be :decode to decode the input string or :encode to
|
94
|
-
# encode the input string. The character set used for encoding will
|
95
|
-
#
|
101
|
+
# encode the input string. The character set used for encoding will be the
|
102
|
+
# encoding on the string passed in.
|
96
103
|
#
|
97
104
|
# On encoding, will only send out Base64 encoded strings.
|
98
105
|
def Encodings.decode_encode(str, output_type)
|
@@ -103,7 +110,7 @@ module Mail
|
|
103
110
|
if str.ascii_only?
|
104
111
|
str
|
105
112
|
else
|
106
|
-
Encodings.b_value_encode(str,
|
113
|
+
Encodings.b_value_encode(str, str.encoding)
|
107
114
|
end
|
108
115
|
end
|
109
116
|
end
|
@@ -114,34 +121,19 @@ module Mail
|
|
114
121
|
# String has to be of the format =?<encoding>?[QB]?<string>?=
|
115
122
|
def Encodings.value_decode(str)
|
116
123
|
# Optimization: If there's no encoded-words in the string, just return it
|
117
|
-
return str unless str =~
|
124
|
+
return str unless str =~ ENCODED_VALUE
|
118
125
|
|
119
126
|
lines = collapse_adjacent_encodings(str)
|
120
127
|
|
121
128
|
# Split on white-space boundaries with capture, so we capture the white-space as well
|
122
|
-
lines.
|
123
|
-
line.
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
# Search for occurences of quoted strings or plain strings
|
128
|
-
text.scan(/( # Group around entire regex to include it in matches
|
129
|
-
\=\?[^?]+\?([QB])\?[^?]+?\?\= # Quoted String with subgroup for encoding method
|
130
|
-
| # or
|
131
|
-
.+?(?=\=\?|$) # Plain String
|
132
|
-
)/xmi).map do |matches|
|
133
|
-
string, method = *matches
|
134
|
-
if method == 'b' || method == 'B'
|
135
|
-
b_value_decode(string)
|
136
|
-
elsif method == 'q' || method == 'Q'
|
137
|
-
q_value_decode(string)
|
138
|
-
else
|
139
|
-
string
|
140
|
-
end
|
141
|
-
end
|
129
|
+
lines.each do |line|
|
130
|
+
line.gsub!(ENCODED_VALUE) do |string|
|
131
|
+
case $2
|
132
|
+
when *B_VALUES then b_value_decode(string)
|
133
|
+
when *Q_VALUES then q_value_decode(string)
|
142
134
|
end
|
143
135
|
end
|
144
|
-
end.
|
136
|
+
end.join("")
|
145
137
|
end
|
146
138
|
|
147
139
|
# Takes an encoded string of the format =?<encoding>?[QB]?<string>?=
|
@@ -152,13 +144,8 @@ module Mail
|
|
152
144
|
output
|
153
145
|
elsif to_encoding
|
154
146
|
begin
|
155
|
-
|
156
|
-
|
157
|
-
else
|
158
|
-
require 'iconv'
|
159
|
-
Iconv.iconv(to_encoding, 'UTF-8', output).first
|
160
|
-
end
|
161
|
-
rescue Iconv::IllegalSequence, Iconv::InvalidEncoding, Errno::EINVAL
|
147
|
+
output.encode(to_encoding)
|
148
|
+
rescue Errno::EINVAL
|
162
149
|
# the 'from' parameter specifies a charset other than what the text
|
163
150
|
# actually is...not much we can do in this case but just return the
|
164
151
|
# unconverted text.
|
@@ -174,21 +161,21 @@ module Mail
|
|
174
161
|
|
175
162
|
def Encodings.address_encode(address, charset = 'utf-8')
|
176
163
|
if address.is_a?(Array)
|
177
|
-
# loop back through for each element
|
178
164
|
address.compact.map { |a| Encodings.address_encode(a, charset) }.join(", ")
|
179
|
-
|
180
|
-
|
181
|
-
encode_non_usascii(address, charset) if address
|
165
|
+
elsif address
|
166
|
+
encode_non_usascii(address, charset)
|
182
167
|
end
|
183
168
|
end
|
184
169
|
|
185
170
|
def Encodings.encode_non_usascii(address, charset)
|
186
171
|
return address if address.ascii_only? or charset.nil?
|
187
|
-
|
188
|
-
# Encode
|
189
|
-
address = address.gsub(/("
|
172
|
+
|
173
|
+
# Encode all strings embedded inside of quotes
|
174
|
+
address = address.gsub(/("[^"]*[^\/]")/) { |s| Encodings.b_value_encode(unquote(s), charset) }
|
175
|
+
|
190
176
|
# Then loop through all remaining items and encode as needed
|
191
177
|
tokens = address.split(/\s/)
|
178
|
+
|
192
179
|
map_with_index(tokens) do |word, i|
|
193
180
|
if word.ascii_only?
|
194
181
|
word
|
@@ -209,12 +196,15 @@ module Mail
|
|
209
196
|
#
|
210
197
|
# Encodings.b_value_encode('This is あ string', 'UTF-8')
|
211
198
|
# #=> "=?UTF-8?B?VGhpcyBpcyDjgYIgc3RyaW5n?="
|
212
|
-
def Encodings.b_value_encode(
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
199
|
+
def Encodings.b_value_encode(string, encoding = nil)
|
200
|
+
if string.to_s.ascii_only?
|
201
|
+
string
|
202
|
+
else
|
203
|
+
Encodings.each_base64_chunk_byterange(string, 60).map do |chunk|
|
204
|
+
str, encoding = Utilities.b_value_encode(chunk, encoding)
|
205
|
+
"=?#{encoding}?B?#{str.chomp}?="
|
206
|
+
end.join(" ")
|
207
|
+
end
|
218
208
|
end
|
219
209
|
|
220
210
|
# Encode a string with Quoted-Printable Encoding and returns it ready to be inserted
|
@@ -226,7 +216,7 @@ module Mail
|
|
226
216
|
# #=> "=?UTF-8?Q?This_is_=E3=81=82_string?="
|
227
217
|
def Encodings.q_value_encode(encoded_str, encoding = nil)
|
228
218
|
return encoded_str if encoded_str.to_s.ascii_only?
|
229
|
-
string, encoding =
|
219
|
+
string, encoding = Utilities.q_value_encode(encoded_str, encoding)
|
230
220
|
string.gsub!("=\r\n", '') # We already have limited the string to the length we want
|
231
221
|
map_lines(string) do |str|
|
232
222
|
"=?#{encoding}?Q?#{str.chomp.gsub(/ /, '_')}?="
|
@@ -242,7 +232,7 @@ module Mail
|
|
242
232
|
# Encodings.b_value_decode("=?UTF-8?B?VGhpcyBpcyDjgYIgc3RyaW5n?=")
|
243
233
|
# #=> 'This is あ string'
|
244
234
|
def Encodings.b_value_decode(str)
|
245
|
-
|
235
|
+
Utilities.b_value_decode(str)
|
246
236
|
end
|
247
237
|
|
248
238
|
# Decodes a Quoted-Printable string from the "=?UTF-8?Q?This_is_=E3=81=82_string?=" format
|
@@ -252,54 +242,73 @@ module Mail
|
|
252
242
|
# Encodings.q_value_decode("=?UTF-8?Q?This_is_=E3=81=82_string?=")
|
253
243
|
# #=> 'This is あ string'
|
254
244
|
def Encodings.q_value_decode(str)
|
255
|
-
|
256
|
-
end
|
257
|
-
|
258
|
-
def Encodings.split_encoding_from_string( str )
|
259
|
-
match = str.match(/\=\?([^?]+)?\?[QB]\?(.+)?\?\=/mi)
|
260
|
-
if match
|
261
|
-
match[1]
|
262
|
-
else
|
263
|
-
nil
|
264
|
-
end
|
265
|
-
end
|
266
|
-
|
267
|
-
def Encodings.find_encoding(str)
|
268
|
-
RUBY_VERSION >= '1.9' ? str.encoding : $KCODE
|
245
|
+
Utilities.q_value_decode(str)
|
269
246
|
end
|
270
247
|
|
271
248
|
# Gets the encoding type (Q or B) from the string.
|
272
|
-
def Encodings.
|
273
|
-
|
274
|
-
if match
|
275
|
-
match[1]
|
276
|
-
else
|
277
|
-
nil
|
278
|
-
end
|
249
|
+
def Encodings.value_encoding_from_string(str)
|
250
|
+
str[ENCODED_VALUE, 1]
|
279
251
|
end
|
280
252
|
|
281
|
-
#
|
282
|
-
# encoding (Q or B) can be joined together.
|
253
|
+
# Split header line into proper encoded and unencoded parts.
|
283
254
|
#
|
284
255
|
# String has to be of the format =?<encoding>?[QB]?<string>?=
|
256
|
+
#
|
257
|
+
# Omit unencoded space after an encoded-word.
|
285
258
|
def Encodings.collapse_adjacent_encodings(str)
|
286
|
-
lines = str.split(/(\?=)\s*(=\?)/).each_slice(2).map(&:join)
|
287
259
|
results = []
|
288
|
-
|
260
|
+
last_encoded = nil # Track whether to preserve or drop whitespace
|
289
261
|
|
290
|
-
lines
|
291
|
-
|
262
|
+
lines = str.split(FULL_ENCODED_VALUE)
|
263
|
+
lines.each_slice(2) do |unencoded, encoded|
|
264
|
+
if last_encoded = encoded
|
265
|
+
if !Utilities.blank?(unencoded) || (!last_encoded && unencoded != EMPTY)
|
266
|
+
results << unencoded
|
267
|
+
end
|
292
268
|
|
293
|
-
|
294
|
-
|
295
|
-
|
269
|
+
results << encoded
|
270
|
+
else
|
271
|
+
results << unencoded
|
296
272
|
end
|
297
|
-
|
298
|
-
previous_encoding = encoding
|
299
|
-
results << line
|
300
273
|
end
|
301
274
|
|
302
275
|
results
|
303
276
|
end
|
277
|
+
|
278
|
+
# Partition the string into bounded-size chunks without splitting
|
279
|
+
# multibyte characters.
|
280
|
+
def Encodings.each_base64_chunk_byterange(str, max_bytesize_per_base64_chunk, &block)
|
281
|
+
raise "size per chunk must be multiple of 4" if (max_bytesize_per_base64_chunk % 4).nonzero?
|
282
|
+
|
283
|
+
if block_given?
|
284
|
+
max_bytesize = ((3 * max_bytesize_per_base64_chunk) / 4.0).floor
|
285
|
+
each_chunk_byterange(str, max_bytesize, &block)
|
286
|
+
else
|
287
|
+
enum_for :each_base64_chunk_byterange, str, max_bytesize_per_base64_chunk
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
# Partition the string into bounded-size chunks without splitting
|
292
|
+
# multibyte characters.
|
293
|
+
def Encodings.each_chunk_byterange(str, max_bytesize_per_chunk)
|
294
|
+
return enum_for(:each_chunk_byterange, str, max_bytesize_per_chunk) unless block_given?
|
295
|
+
|
296
|
+
offset = 0
|
297
|
+
chunksize = 0
|
298
|
+
|
299
|
+
str.each_char do |chr|
|
300
|
+
charsize = chr.bytesize
|
301
|
+
|
302
|
+
if chunksize + charsize > max_bytesize_per_chunk
|
303
|
+
yield Utilities.string_byteslice(str, offset, chunksize)
|
304
|
+
offset += chunksize
|
305
|
+
chunksize = charsize
|
306
|
+
else
|
307
|
+
chunksize += charsize
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
yield Utilities.string_byteslice(str, offset, chunksize)
|
312
|
+
end
|
304
313
|
end
|
305
314
|
end
|
data/lib/mail/envelope.rb
CHANGED
@@ -1,35 +1,28 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
#
|
2
|
+
# frozen_string_literal: true
|
3
|
+
#
|
3
4
|
# = Mail Envelope
|
4
|
-
#
|
5
|
+
#
|
5
6
|
# The Envelope class provides a field for the first line in an
|
6
7
|
# mbox file, that looks like "From mikel@test.lindsaar.net DATETIME"
|
7
|
-
#
|
8
|
+
#
|
8
9
|
# This envelope class reads that line, and turns it into an
|
9
10
|
# Envelope.from and Envelope.date for your use.
|
11
|
+
|
10
12
|
module Mail
|
11
|
-
class Envelope <
|
12
|
-
|
13
|
-
|
14
|
-
super(FIELD_NAME, strip_field(FIELD_NAME, args.last))
|
15
|
-
end
|
16
|
-
|
17
|
-
def tree
|
18
|
-
@element ||= Mail::EnvelopeFromElement.new(value)
|
19
|
-
@tree ||= @element.tree
|
20
|
-
end
|
21
|
-
|
13
|
+
class Envelope < NamedStructuredField
|
14
|
+
NAME = 'Envelope-From'
|
15
|
+
|
22
16
|
def element
|
23
17
|
@element ||= Mail::EnvelopeFromElement.new(value)
|
24
18
|
end
|
25
|
-
|
26
|
-
def date
|
27
|
-
::DateTime.parse("#{element.date_time}")
|
28
|
-
end
|
29
19
|
|
30
20
|
def from
|
31
21
|
element.address
|
32
22
|
end
|
33
|
-
|
23
|
+
|
24
|
+
def date
|
25
|
+
element.date_time
|
26
|
+
end
|
34
27
|
end
|
35
28
|
end
|