mail 2.6.1 → 2.7.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 +92 -80
- data/lib/mail/attachments_list.rb +11 -5
- data/lib/mail/body.rb +81 -44
- data/lib/mail/check_delivery_params.rb +50 -10
- data/lib/mail/configuration.rb +3 -0
- data/lib/mail/{patterns.rb → constants.rb} +26 -6
- data/lib/mail/core_extensions/smtp.rb +20 -16
- data/lib/mail/core_extensions/string.rb +1 -27
- data/lib/mail/elements/address.rb +81 -93
- data/lib/mail/elements/address_list.rb +12 -29
- data/lib/mail/elements/content_disposition_element.rb +9 -15
- data/lib/mail/elements/content_location_element.rb +8 -12
- data/lib/mail/elements/content_transfer_encoding_element.rb +6 -10
- data/lib/mail/elements/content_type_element.rb +9 -19
- data/lib/mail/elements/date_time_element.rb +7 -14
- data/lib/mail/elements/envelope_from_element.rb +15 -21
- data/lib/mail/elements/message_ids_element.rb +12 -14
- data/lib/mail/elements/mime_version_element.rb +7 -14
- data/lib/mail/elements/phrase_list.rb +7 -9
- data/lib/mail/elements/received_element.rb +10 -15
- data/lib/mail/elements.rb +1 -0
- data/lib/mail/encodings/7bit.rb +6 -15
- 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 +121 -82
- data/lib/mail/envelope.rb +2 -1
- data/lib/mail/field.rb +114 -62
- data/lib/mail/field_list.rb +2 -1
- data/lib/mail/fields/bcc_field.rb +17 -5
- data/lib/mail/fields/cc_field.rb +2 -2
- data/lib/mail/fields/comments_field.rb +2 -1
- data/lib/mail/fields/common/address_container.rb +3 -2
- data/lib/mail/fields/common/common_address.rb +40 -14
- data/lib/mail/fields/common/common_date.rb +2 -1
- data/lib/mail/fields/common/common_field.rb +6 -11
- data/lib/mail/fields/common/common_message_id.rb +3 -2
- data/lib/mail/fields/common/parameter_hash.rb +5 -4
- data/lib/mail/fields/content_description_field.rb +2 -1
- data/lib/mail/fields/content_disposition_field.rb +14 -13
- data/lib/mail/fields/content_id_field.rb +5 -4
- data/lib/mail/fields/content_location_field.rb +3 -2
- data/lib/mail/fields/content_transfer_encoding_field.rb +3 -2
- data/lib/mail/fields/content_type_field.rb +7 -11
- data/lib/mail/fields/date_field.rb +4 -4
- 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 +3 -3
- data/lib/mail/fields/message_id_field.rb +3 -2
- data/lib/mail/fields/mime_version_field.rb +4 -3
- data/lib/mail/fields/optional_field.rb +5 -1
- data/lib/mail/fields/received_field.rb +5 -4
- 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_date_field.rb +2 -2
- data/lib/mail/fields/resent_from_field.rb +2 -2
- data/lib/mail/fields/resent_message_id_field.rb +2 -1
- data/lib/mail/fields/resent_sender_field.rb +2 -2
- data/lib/mail/fields/resent_to_field.rb +2 -2
- data/lib/mail/fields/return_path_field.rb +2 -2
- data/lib/mail/fields/sender_field.rb +2 -2
- data/lib/mail/fields/structured_field.rb +1 -0
- data/lib/mail/fields/subject_field.rb +2 -1
- data/lib/mail/fields/to_field.rb +2 -2
- data/lib/mail/fields/unstructured_field.rb +28 -10
- data/lib/mail/fields.rb +1 -0
- data/lib/mail/header.rb +18 -14
- data/lib/mail/indifferent_hash.rb +1 -0
- data/lib/mail/mail.rb +6 -11
- data/lib/mail/matchers/attachment_matchers.rb +29 -0
- data/lib/mail/matchers/has_sent_mail.rb +53 -9
- data/lib/mail/message.rb +99 -89
- data/lib/mail/multibyte/chars.rb +32 -30
- data/lib/mail/multibyte/unicode.rb +31 -26
- data/lib/mail/multibyte/utils.rb +1 -0
- data/lib/mail/multibyte.rb +65 -15
- data/lib/mail/network/delivery_methods/exim.rb +7 -10
- data/lib/mail/network/delivery_methods/file_delivery.rb +5 -8
- data/lib/mail/network/delivery_methods/logger_delivery.rb +37 -0
- data/lib/mail/network/delivery_methods/sendmail.rb +17 -11
- data/lib/mail/network/delivery_methods/smtp.rb +60 -53
- data/lib/mail/network/delivery_methods/smtp_connection.rb +11 -6
- data/lib/mail/network/delivery_methods/test_mailer.rb +6 -8
- data/lib/mail/network/retriever_methods/base.rb +1 -0
- data/lib/mail/network/retriever_methods/imap.rb +19 -5
- data/lib/mail/network/retriever_methods/pop3.rb +4 -1
- data/lib/mail/network/retriever_methods/test_retriever.rb +2 -1
- data/lib/mail/network.rb +2 -0
- data/lib/mail/parser_tools.rb +15 -0
- data/lib/mail/parsers/address_lists_parser.rb +33208 -104
- data/lib/mail/parsers/address_lists_parser.rl +172 -0
- data/lib/mail/parsers/content_disposition_parser.rb +877 -49
- data/lib/mail/parsers/content_disposition_parser.rl +82 -0
- data/lib/mail/parsers/content_location_parser.rb +804 -23
- data/lib/mail/parsers/content_location_parser.rl +71 -0
- data/lib/mail/parsers/content_transfer_encoding_parser.rb +502 -19
- data/lib/mail/parsers/content_transfer_encoding_parser.rl +64 -0
- data/lib/mail/parsers/content_type_parser.rb +1024 -46
- data/lib/mail/parsers/content_type_parser.rl +83 -0
- data/lib/mail/parsers/date_time_parser.rb +872 -23
- data/lib/mail/parsers/date_time_parser.rl +62 -0
- data/lib/mail/parsers/envelope_from_parser.rb +3570 -34
- data/lib/mail/parsers/envelope_from_parser.rl +82 -0
- data/lib/mail/parsers/message_ids_parser.rb +2840 -25
- data/lib/mail/parsers/message_ids_parser.rl +82 -0
- data/lib/mail/parsers/mime_version_parser.rb +492 -26
- data/lib/mail/parsers/mime_version_parser.rl +61 -0
- data/lib/mail/parsers/phrase_lists_parser.rb +862 -17
- data/lib/mail/parsers/phrase_lists_parser.rl +83 -0
- data/lib/mail/parsers/received_parser.rb +8765 -36
- data/lib/mail/parsers/received_parser.rl +84 -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 +59 -0
- data/lib/mail/parsers/rfc5322_address.rl +72 -0
- data/lib/mail/parsers/{ragel/date_time.rl → rfc5322_date_time.rl} +8 -1
- data/lib/mail/parsers/rfc5322_lexical_tokens.rl +60 -0
- data/lib/mail/parsers.rb +17 -24
- data/lib/mail/part.rb +8 -5
- data/lib/mail/parts_list.rb +31 -14
- data/lib/mail/utilities.rb +112 -13
- 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 +52 -8
- data/lib/mail/version_specific/ruby_1_9.rb +143 -24
- data/lib/mail.rb +8 -14
- metadata +71 -81
- data/CHANGELOG.rdoc +0 -752
- data/CONTRIBUTING.md +0 -60
- data/Dependencies.txt +0 -2
- data/Gemfile +0 -15
- data/Rakefile +0 -29
- data/TODO.rdoc +0 -9
- data/VERSION +0 -4
- data/lib/mail/core_extensions/nil.rb +0 -19
- data/lib/mail/core_extensions/object.rb +0 -13
- data/lib/mail/core_extensions/string/access.rb +0 -145
- data/lib/mail/core_extensions/string/multibyte.rb +0 -78
- data/lib/mail/multibyte/exceptions.rb +0 -8
- data/lib/mail/parsers/ragel/common.rl +0 -184
- data/lib/mail/parsers/ragel/parser_info.rb +0 -61
- data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +0 -14864
- data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +0 -751
- data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +0 -614
- data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +0 -447
- data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +0 -825
- data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +0 -817
- data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +0 -2129
- data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +0 -1570
- data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +0 -440
- data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +0 -564
- data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +0 -51
- data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +0 -5144
- data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +0 -37
- data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +0 -37
- data/lib/mail/parsers/ragel/ruby.rb +0 -39
- data/lib/mail/parsers/ragel.rb +0 -17
@@ -1,21 +1,17 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
require 'mail/parsers/content_location_parser'
|
4
|
+
|
2
5
|
module Mail
|
3
6
|
class ContentLocationElement # :nodoc:
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
content_location = Mail::Parsers::ContentLocationParser.new.parse(string)
|
9
|
-
@location = content_location.location
|
7
|
+
attr_reader :location
|
8
|
+
|
9
|
+
def initialize(string)
|
10
|
+
@location = Mail::Parsers::ContentLocationParser.parse(string).location
|
10
11
|
end
|
11
|
-
|
12
|
-
def location
|
13
|
-
@location
|
14
|
-
end
|
15
|
-
|
12
|
+
|
16
13
|
def to_s(*args)
|
17
14
|
location.to_s
|
18
15
|
end
|
19
|
-
|
20
16
|
end
|
21
17
|
end
|
@@ -1,17 +1,13 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
require 'mail/parsers/content_transfer_encoding_parser'
|
4
|
+
|
2
5
|
module Mail
|
3
6
|
class ContentTransferEncodingElement
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
+
attr_reader :encoding
|
8
|
+
|
7
9
|
def initialize(string)
|
8
|
-
|
9
|
-
@encoding = content_transfer_encoding.encoding
|
10
|
+
@encoding = Mail::Parsers::ContentTransferEncodingParser.parse(string).encoding
|
10
11
|
end
|
11
|
-
|
12
|
-
def encoding
|
13
|
-
@encoding
|
14
|
-
end
|
15
|
-
|
16
12
|
end
|
17
13
|
end
|
@@ -1,31 +1,21 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
require 'mail/parsers/content_type_parser'
|
4
|
+
|
2
5
|
module Mail
|
3
6
|
class ContentTypeElement # :nodoc:
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
content_type = Mail::Parsers::ContentTypeParser.new.parse(cleaned(string))
|
7
|
+
attr_reader :main_type, :sub_type, :parameters
|
8
|
+
|
9
|
+
def initialize(string)
|
10
|
+
content_type = Mail::Parsers::ContentTypeParser.parse(cleaned(string))
|
9
11
|
@main_type = content_type.main_type
|
10
12
|
@sub_type = content_type.sub_type
|
11
13
|
@parameters = content_type.parameters
|
12
14
|
end
|
13
|
-
|
14
|
-
|
15
|
-
@main_type
|
16
|
-
end
|
17
|
-
|
18
|
-
def sub_type
|
19
|
-
@sub_type
|
20
|
-
end
|
21
|
-
|
22
|
-
def parameters
|
23
|
-
@parameters
|
24
|
-
end
|
25
|
-
|
15
|
+
|
16
|
+
private
|
26
17
|
def cleaned(string)
|
27
18
|
string =~ /(.+);\s*$/ ? $1 : string
|
28
19
|
end
|
29
|
-
|
30
20
|
end
|
31
21
|
end
|
@@ -1,22 +1,15 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
require 'mail/parsers/date_time_parser'
|
4
|
+
|
2
5
|
module Mail
|
3
6
|
class DateTimeElement # :nodoc:
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
date_time = Mail::Parsers::DateTimeParser.new.parse(string)
|
7
|
+
attr_reader :date_string, :time_string
|
8
|
+
|
9
|
+
def initialize(string)
|
10
|
+
date_time = Mail::Parsers::DateTimeParser.parse(string)
|
9
11
|
@date_string = date_time.date_string
|
10
12
|
@time_string = date_time.time_string
|
11
13
|
end
|
12
|
-
|
13
|
-
def date_string
|
14
|
-
@date_string
|
15
|
-
end
|
16
|
-
|
17
|
-
def time_string
|
18
|
-
@time_string
|
19
|
-
end
|
20
|
-
|
21
14
|
end
|
22
15
|
end
|
@@ -1,39 +1,33 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
require 'mail/parsers/envelope_from_parser'
|
4
|
+
require 'date'
|
5
|
+
|
2
6
|
module Mail
|
3
7
|
class EnvelopeFromElement
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
@
|
9
|
-
@
|
10
|
-
@date_time = ::DateTime.parse(@envelope_from.ctime_date)
|
11
|
-
end
|
12
|
-
|
13
|
-
def date_time
|
14
|
-
@date_time
|
15
|
-
end
|
16
|
-
|
17
|
-
def address
|
18
|
-
@address
|
8
|
+
attr_reader :date_time, :address
|
9
|
+
|
10
|
+
def initialize(string)
|
11
|
+
envelope_from = Mail::Parsers::EnvelopeFromParser.parse(string)
|
12
|
+
@address = envelope_from.address
|
13
|
+
@date_time = ::DateTime.parse(envelope_from.ctime_date)
|
19
14
|
end
|
20
|
-
|
15
|
+
|
21
16
|
# RFC 4155:
|
22
17
|
# a timestamp indicating the UTC date and time when the message
|
23
18
|
# was originally received, conformant with the syntax of the
|
24
19
|
# traditional UNIX 'ctime' output sans timezone (note that the
|
25
20
|
# use of UTC precludes the need for a timezone indicator);
|
26
21
|
def formatted_date_time
|
27
|
-
if
|
28
|
-
|
22
|
+
if date_time.respond_to?(:ctime)
|
23
|
+
date_time.ctime
|
29
24
|
else
|
30
|
-
|
25
|
+
date_time.strftime '%a %b %e %T %Y'
|
31
26
|
end
|
32
27
|
end
|
33
28
|
|
34
29
|
def to_s
|
35
|
-
"#{
|
30
|
+
"#{address} #{formatted_date_time}"
|
36
31
|
end
|
37
|
-
|
38
32
|
end
|
39
33
|
end
|
@@ -1,24 +1,22 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
require 'mail/parsers/message_ids_parser'
|
4
|
+
|
2
5
|
module Mail
|
3
6
|
class MessageIdsElement
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
+
attr_reader :message_ids
|
8
|
+
|
7
9
|
def initialize(string)
|
8
|
-
@message_ids = Mail::Parsers::MessageIdsParser.
|
9
|
-
end
|
10
|
-
|
11
|
-
def message_ids
|
12
|
-
@message_ids
|
10
|
+
@message_ids = Mail::Parsers::MessageIdsParser.parse(string).message_ids.map { |msg_id| clean_msg_id(msg_id) }
|
13
11
|
end
|
14
|
-
|
12
|
+
|
15
13
|
def message_id
|
16
|
-
|
17
|
-
end
|
18
|
-
|
19
|
-
def clean_msg_id( val )
|
20
|
-
val =~ /.*<(.*)>.*/ ; $1
|
14
|
+
message_ids.first
|
21
15
|
end
|
22
16
|
|
17
|
+
private
|
18
|
+
def clean_msg_id(val)
|
19
|
+
val =~ /.*<(.*)>.*/ ? $1 : val
|
20
|
+
end
|
23
21
|
end
|
24
22
|
end
|
@@ -1,22 +1,15 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
require 'mail/parsers/mime_version_parser'
|
4
|
+
|
2
5
|
module Mail
|
3
6
|
class MimeVersionElement
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
mime_version = Mail::Parsers::MimeVersionParser.new.parse(string)
|
7
|
+
attr_reader :major, :minor
|
8
|
+
|
9
|
+
def initialize(string)
|
10
|
+
mime_version = Mail::Parsers::MimeVersionParser.parse(string)
|
9
11
|
@major = mime_version.major
|
10
12
|
@minor = mime_version.minor
|
11
13
|
end
|
12
|
-
|
13
|
-
def major
|
14
|
-
@major
|
15
|
-
end
|
16
|
-
|
17
|
-
def minor
|
18
|
-
@minor
|
19
|
-
end
|
20
|
-
|
21
14
|
end
|
22
15
|
end
|
@@ -1,16 +1,14 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
require 'mail/parsers/phrase_lists_parser'
|
4
|
+
require 'mail/utilities'
|
5
|
+
|
2
6
|
module Mail
|
3
7
|
class PhraseList
|
4
|
-
|
5
|
-
|
6
|
-
|
8
|
+
attr_reader :phrases
|
9
|
+
|
7
10
|
def initialize(string)
|
8
|
-
@
|
11
|
+
@phrases = Mail::Parsers::PhraseListsParser.parse(string).phrases.map { |p| Mail::Utilities.unquote(p) }
|
9
12
|
end
|
10
|
-
|
11
|
-
def phrases
|
12
|
-
@phrase_lists.phrases.map { |p| unquote(p) }
|
13
|
-
end
|
14
|
-
|
15
13
|
end
|
16
14
|
end
|
@@ -1,26 +1,21 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
require 'mail/parsers/received_parser'
|
4
|
+
require 'date'
|
5
|
+
|
2
6
|
module Mail
|
3
7
|
class ReceivedElement
|
4
|
-
|
5
8
|
include Mail::Utilities
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
+
attr_reader :date_time, :info
|
10
|
+
|
11
|
+
def initialize(string)
|
12
|
+
received = Mail::Parsers::ReceivedParser.parse(string)
|
9
13
|
@date_time = ::DateTime.parse("#{received.date} #{received.time}")
|
10
14
|
@info = received.info
|
11
15
|
end
|
12
|
-
|
13
|
-
def date_time
|
14
|
-
@date_time
|
15
|
-
end
|
16
|
-
|
17
|
-
def info
|
18
|
-
@info
|
19
|
-
end
|
20
|
-
|
16
|
+
|
21
17
|
def to_s(*args)
|
22
|
-
"#{
|
18
|
+
"#{info}; #{date_time.to_s(*args)}"
|
23
19
|
end
|
24
|
-
|
25
20
|
end
|
26
21
|
end
|
data/lib/mail/elements.rb
CHANGED
data/lib/mail/encodings/7bit.rb
CHANGED
@@ -1,31 +1,22 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
2
3
|
require 'mail/encodings/8bit'
|
3
4
|
|
4
5
|
module Mail
|
5
6
|
module Encodings
|
7
|
+
# 7bit and 8bit are equivalent. 7bit encoding is for text only.
|
6
8
|
class SevenBit < EightBit
|
7
9
|
NAME = '7bit'
|
8
|
-
|
9
10
|
PRIORITY = 1
|
11
|
+
Encodings.register(NAME, self)
|
10
12
|
|
11
|
-
# 7bit and 8bit operate the same
|
12
|
-
|
13
|
-
# Decode the string
|
14
13
|
def self.decode(str)
|
15
|
-
|
14
|
+
::Mail::Utilities.binary_unsafe_to_lf str
|
16
15
|
end
|
17
|
-
|
18
|
-
# Encode the string
|
16
|
+
|
19
17
|
def self.encode(str)
|
20
|
-
|
21
|
-
end
|
22
|
-
|
23
|
-
# Idenity encodings have a fixed cost, 1 byte out per 1 byte in
|
24
|
-
def self.cost(str)
|
25
|
-
super
|
18
|
+
::Mail::Utilities.binary_unsafe_to_crlf str
|
26
19
|
end
|
27
|
-
|
28
|
-
Encodings.register(NAME, self)
|
29
20
|
end
|
30
21
|
end
|
31
22
|
end
|
data/lib/mail/encodings/8bit.rb
CHANGED
@@ -1,31 +1,18 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
2
3
|
require 'mail/encodings/binary'
|
3
4
|
|
4
5
|
module Mail
|
5
6
|
module Encodings
|
6
7
|
class EightBit < Binary
|
7
8
|
NAME = '8bit'
|
8
|
-
|
9
9
|
PRIORITY = 4
|
10
|
+
Encodings.register(NAME, self)
|
10
11
|
|
11
|
-
#
|
12
|
-
|
13
|
-
|
14
|
-
def self.decode(str)
|
15
|
-
str.to_lf
|
16
|
-
end
|
17
|
-
|
18
|
-
# Encode the string
|
19
|
-
def self.encode(str)
|
20
|
-
str.to_crlf
|
12
|
+
# Per RFC 2821 4.5.3.1, SMTP lines may not be longer than 1000 octets including the <CRLF>.
|
13
|
+
def self.compatible_input?(str)
|
14
|
+
!str.lines.find { |line| line.bytesize > 998 }
|
21
15
|
end
|
22
|
-
|
23
|
-
# Idenity encodings have a fixed cost, 1 byte out per 1 byte in
|
24
|
-
def self.cost(str)
|
25
|
-
1.0
|
26
|
-
end
|
27
|
-
|
28
|
-
Encodings.register(NAME, self)
|
29
16
|
end
|
30
17
|
end
|
31
18
|
end
|
@@ -1,33 +1,38 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
2
3
|
require 'mail/encodings/7bit'
|
3
4
|
|
4
5
|
module Mail
|
5
6
|
module Encodings
|
7
|
+
# Base64 encoding handles binary content at the cost of 4 output bytes
|
8
|
+
# per input byte.
|
6
9
|
class Base64 < SevenBit
|
7
10
|
NAME = 'base64'
|
8
|
-
|
9
11
|
PRIORITY = 3
|
10
|
-
|
12
|
+
Encodings.register(NAME, self)
|
13
|
+
|
11
14
|
def self.can_encode?(enc)
|
12
15
|
true
|
13
16
|
end
|
14
17
|
|
15
|
-
# Decode the string from Base64
|
16
18
|
def self.decode(str)
|
17
|
-
RubyVer.decode_base64(
|
19
|
+
RubyVer.decode_base64(str)
|
18
20
|
end
|
19
|
-
|
20
|
-
# Encode the string to Base64
|
21
|
+
|
21
22
|
def self.encode(str)
|
22
|
-
RubyVer.encode_base64(
|
23
|
+
::Mail::Utilities.binary_unsafe_to_crlf(RubyVer.encode_base64(str))
|
23
24
|
end
|
24
25
|
|
25
|
-
#
|
26
|
+
# 3 bytes in -> 4 bytes out
|
26
27
|
def self.cost(str)
|
27
|
-
4.0/3
|
28
|
+
4.0 / 3
|
28
29
|
end
|
29
30
|
|
30
|
-
|
31
|
+
# Ruby Base64 inserts newlines automatically, so it doesn't exceed
|
32
|
+
# SMTP line length limits.
|
33
|
+
def self.compatible_input?(str)
|
34
|
+
true
|
35
|
+
end
|
31
36
|
end
|
32
37
|
end
|
33
38
|
end
|
@@ -1,31 +1,13 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
require 'mail/encodings/identity'
|
3
4
|
|
4
5
|
module Mail
|
5
6
|
module Encodings
|
6
|
-
class Binary <
|
7
|
+
class Binary < Identity
|
7
8
|
NAME = 'binary'
|
8
|
-
|
9
9
|
PRIORITY = 5
|
10
|
-
|
11
|
-
# Binary is an identiy encoding, meaning nothing to do
|
12
|
-
|
13
|
-
# Decode the string
|
14
|
-
def self.decode(str)
|
15
|
-
str
|
16
|
-
end
|
17
|
-
|
18
|
-
# Encode the string
|
19
|
-
def self.encode(str)
|
20
|
-
str
|
21
|
-
end
|
22
|
-
|
23
|
-
# Idenity encodings have a fixed cost, 1 byte out per 1 byte in
|
24
|
-
def self.cost(str)
|
25
|
-
1.0
|
26
|
-
end
|
27
|
-
|
28
|
-
Encodings.register(NAME, self)
|
10
|
+
Encodings.register(NAME, self)
|
29
11
|
end
|
30
12
|
end
|
31
13
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
require 'mail/encodings/transfer_encoding'
|
4
|
+
|
5
|
+
module Mail
|
6
|
+
module Encodings
|
7
|
+
# Identity encodings do no encoding/decoding and have a fixed cost:
|
8
|
+
# 1 byte in -> 1 byte out.
|
9
|
+
class Identity < TransferEncoding #:nodoc:
|
10
|
+
def self.decode(str)
|
11
|
+
str
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.encode(str)
|
15
|
+
str
|
16
|
+
end
|
17
|
+
|
18
|
+
# 1 output byte per input byte.
|
19
|
+
def self.cost(str)
|
20
|
+
1.0
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -1,36 +1,42 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
2
3
|
require 'mail/encodings/7bit'
|
3
4
|
|
4
5
|
module Mail
|
5
6
|
module Encodings
|
6
7
|
class QuotedPrintable < SevenBit
|
7
8
|
NAME='quoted-printable'
|
8
|
-
|
9
|
+
|
9
10
|
PRIORITY = 2
|
10
11
|
|
11
|
-
def self.can_encode?(
|
12
|
-
EightBit.can_encode?
|
12
|
+
def self.can_encode?(enc)
|
13
|
+
EightBit.can_encode? enc
|
13
14
|
end
|
14
15
|
|
15
16
|
# Decode the string from Quoted-Printable. Cope with hard line breaks
|
16
17
|
# that were incorrectly encoded as hex instead of literal CRLF.
|
17
18
|
def self.decode(str)
|
18
|
-
str.gsub(/(?:=0D=0A|=0D|=0A)\r\n/, "\r\n").unpack("M*").first
|
19
|
+
str.gsub(/(?:=0D=0A|=0D|=0A)\r\n/, "\r\n").unpack("M*").first
|
19
20
|
end
|
20
21
|
|
21
22
|
def self.encode(str)
|
22
|
-
[str
|
23
|
+
[str].pack("M")
|
23
24
|
end
|
24
25
|
|
25
26
|
def self.cost(str)
|
26
27
|
# These bytes probably do not need encoding
|
27
28
|
c = str.count("\x9\xA\xD\x20-\x3C\x3E-\x7E")
|
28
|
-
# Everything else turns into =XX where XX is a
|
29
|
+
# Everything else turns into =XX where XX is a
|
29
30
|
# two digit hex number (taking 3 bytes)
|
30
31
|
total = (str.bytesize - c)*3 + c
|
31
32
|
total.to_f/str.bytesize
|
32
33
|
end
|
33
|
-
|
34
|
+
|
35
|
+
# QP inserts newlines automatically and cannot violate the SMTP spec.
|
36
|
+
def self.compatible_input?(str)
|
37
|
+
true
|
38
|
+
end
|
39
|
+
|
34
40
|
private
|
35
41
|
|
36
42
|
Encodings.register(NAME, self)
|
@@ -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
|