nano-fast-tool 0.0.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 +7 -0
- data/mail-2.9.1/MIT-LICENSE +20 -0
- data/mail-2.9.1/README.md +771 -0
- data/mail-2.9.1/lib/mail/attachments_list.rb +109 -0
- data/mail-2.9.1/lib/mail/body.rb +305 -0
- data/mail-2.9.1/lib/mail/configuration.rb +78 -0
- data/mail-2.9.1/lib/mail/constants.rb +79 -0
- data/mail-2.9.1/lib/mail/elements/address.rb +258 -0
- data/mail-2.9.1/lib/mail/elements/address_list.rb +34 -0
- data/mail-2.9.1/lib/mail/elements/content_disposition_element.rb +20 -0
- data/mail-2.9.1/lib/mail/elements/content_location_element.rb +17 -0
- data/mail-2.9.1/lib/mail/elements/content_transfer_encoding_element.rb +13 -0
- data/mail-2.9.1/lib/mail/elements/content_type_element.rb +25 -0
- data/mail-2.9.1/lib/mail/elements/date_time_element.rb +15 -0
- data/mail-2.9.1/lib/mail/elements/envelope_from_element.rb +39 -0
- data/mail-2.9.1/lib/mail/elements/message_ids_element.rb +31 -0
- data/mail-2.9.1/lib/mail/elements/mime_version_element.rb +15 -0
- data/mail-2.9.1/lib/mail/elements/phrase_list.rb +19 -0
- data/mail-2.9.1/lib/mail/elements/received_element.rb +35 -0
- data/mail-2.9.1/lib/mail/elements.rb +15 -0
- data/mail-2.9.1/lib/mail/encodings/7bit.rb +27 -0
- data/mail-2.9.1/lib/mail/encodings/8bit.rb +18 -0
- data/mail-2.9.1/lib/mail/encodings/base64.rb +38 -0
- data/mail-2.9.1/lib/mail/encodings/binary.rb +13 -0
- data/mail-2.9.1/lib/mail/encodings/identity.rb +24 -0
- data/mail-2.9.1/lib/mail/encodings/quoted_printable.rb +45 -0
- data/mail-2.9.1/lib/mail/encodings/transfer_encoding.rb +77 -0
- data/mail-2.9.1/lib/mail/encodings/unix_to_unix.rb +20 -0
- data/mail-2.9.1/lib/mail/encodings.rb +314 -0
- data/mail-2.9.1/lib/mail/envelope.rb +28 -0
- data/mail-2.9.1/lib/mail/field.rb +313 -0
- data/mail-2.9.1/lib/mail/field_list.rb +87 -0
- data/mail-2.9.1/lib/mail/fields/bcc_field.rb +49 -0
- data/mail-2.9.1/lib/mail/fields/cc_field.rb +33 -0
- data/mail-2.9.1/lib/mail/fields/comments_field.rb +31 -0
- data/mail-2.9.1/lib/mail/fields/common_address_field.rb +162 -0
- data/mail-2.9.1/lib/mail/fields/common_date_field.rb +56 -0
- data/mail-2.9.1/lib/mail/fields/common_field.rb +77 -0
- data/mail-2.9.1/lib/mail/fields/common_message_id_field.rb +41 -0
- data/mail-2.9.1/lib/mail/fields/content_description_field.rb +12 -0
- data/mail-2.9.1/lib/mail/fields/content_disposition_field.rb +44 -0
- data/mail-2.9.1/lib/mail/fields/content_id_field.rb +35 -0
- data/mail-2.9.1/lib/mail/fields/content_location_field.rb +28 -0
- data/mail-2.9.1/lib/mail/fields/content_transfer_encoding_field.rb +44 -0
- data/mail-2.9.1/lib/mail/fields/content_type_field.rb +171 -0
- data/mail-2.9.1/lib/mail/fields/date_field.rb +28 -0
- data/mail-2.9.1/lib/mail/fields/from_field.rb +33 -0
- data/mail-2.9.1/lib/mail/fields/in_reply_to_field.rb +45 -0
- data/mail-2.9.1/lib/mail/fields/keywords_field.rb +30 -0
- data/mail-2.9.1/lib/mail/fields/message_id_field.rb +36 -0
- data/mail-2.9.1/lib/mail/fields/mime_version_field.rb +42 -0
- data/mail-2.9.1/lib/mail/fields/named_structured_field.rb +10 -0
- data/mail-2.9.1/lib/mail/fields/named_unstructured_field.rb +10 -0
- data/mail-2.9.1/lib/mail/fields/optional_field.rb +15 -0
- data/mail-2.9.1/lib/mail/fields/parameter_hash.rb +61 -0
- data/mail-2.9.1/lib/mail/fields/received_field.rb +61 -0
- data/mail-2.9.1/lib/mail/fields/references_field.rb +42 -0
- data/mail-2.9.1/lib/mail/fields/reply_to_field.rb +33 -0
- data/mail-2.9.1/lib/mail/fields/resent_bcc_field.rb +33 -0
- data/mail-2.9.1/lib/mail/fields/resent_cc_field.rb +33 -0
- data/mail-2.9.1/lib/mail/fields/resent_date_field.rb +10 -0
- data/mail-2.9.1/lib/mail/fields/resent_from_field.rb +33 -0
- data/mail-2.9.1/lib/mail/fields/resent_message_id_field.rb +10 -0
- data/mail-2.9.1/lib/mail/fields/resent_sender_field.rb +32 -0
- data/mail-2.9.1/lib/mail/fields/resent_to_field.rb +33 -0
- data/mail-2.9.1/lib/mail/fields/return_path_field.rb +60 -0
- data/mail-2.9.1/lib/mail/fields/sender_field.rb +45 -0
- data/mail-2.9.1/lib/mail/fields/structured_field.rb +24 -0
- data/mail-2.9.1/lib/mail/fields/subject_field.rb +14 -0
- data/mail-2.9.1/lib/mail/fields/to_field.rb +33 -0
- data/mail-2.9.1/lib/mail/fields/unstructured_field.rb +189 -0
- data/mail-2.9.1/lib/mail/fields.rb +45 -0
- data/mail-2.9.1/lib/mail/header.rb +237 -0
- data/mail-2.9.1/lib/mail/indifferent_hash.rb +147 -0
- data/mail-2.9.1/lib/mail/mail.rb +267 -0
- data/mail-2.9.1/lib/mail/matchers/attachment_matchers.rb +44 -0
- data/mail-2.9.1/lib/mail/matchers/has_sent_mail.rb +201 -0
- data/mail-2.9.1/lib/mail/message.rb +2159 -0
- data/mail-2.9.1/lib/mail/multibyte/chars.rb +318 -0
- data/mail-2.9.1/lib/mail/multibyte/unicode.rb +405 -0
- data/mail-2.9.1/lib/mail/multibyte/utils.rb +44 -0
- data/mail-2.9.1/lib/mail/multibyte.rb +82 -0
- data/mail-2.9.1/lib/mail/network/delivery_methods/exim.rb +50 -0
- data/mail-2.9.1/lib/mail/network/delivery_methods/file_delivery.rb +43 -0
- data/mail-2.9.1/lib/mail/network/delivery_methods/logger_delivery.rb +34 -0
- data/mail-2.9.1/lib/mail/network/delivery_methods/sendmail.rb +87 -0
- data/mail-2.9.1/lib/mail/network/delivery_methods/smtp.rb +200 -0
- data/mail-2.9.1/lib/mail/network/delivery_methods/smtp_connection.rb +57 -0
- data/mail-2.9.1/lib/mail/network/delivery_methods/test_mailer.rb +44 -0
- data/mail-2.9.1/lib/mail/network/retriever_methods/base.rb +64 -0
- data/mail-2.9.1/lib/mail/network/retriever_methods/imap.rb +190 -0
- data/mail-2.9.1/lib/mail/network/retriever_methods/pop3.rb +143 -0
- data/mail-2.9.1/lib/mail/network/retriever_methods/test_retriever.rb +45 -0
- data/mail-2.9.1/lib/mail/network.rb +16 -0
- data/mail-2.9.1/lib/mail/parser_tools.rb +15 -0
- data/mail-2.9.1/lib/mail/parsers/address_lists_parser.rb +33245 -0
- data/mail-2.9.1/lib/mail/parsers/address_lists_parser.rl +183 -0
- data/mail-2.9.1/lib/mail/parsers/content_disposition_parser.rb +904 -0
- data/mail-2.9.1/lib/mail/parsers/content_disposition_parser.rl +93 -0
- data/mail-2.9.1/lib/mail/parsers/content_location_parser.rb +825 -0
- data/mail-2.9.1/lib/mail/parsers/content_location_parser.rl +82 -0
- data/mail-2.9.1/lib/mail/parsers/content_transfer_encoding_parser.rb +525 -0
- data/mail-2.9.1/lib/mail/parsers/content_transfer_encoding_parser.rl +75 -0
- data/mail-2.9.1/lib/mail/parsers/content_type_parser.rb +1051 -0
- data/mail-2.9.1/lib/mail/parsers/content_type_parser.rl +94 -0
- data/mail-2.9.1/lib/mail/parsers/date_time_parser.rb +894 -0
- data/mail-2.9.1/lib/mail/parsers/date_time_parser.rl +73 -0
- data/mail-2.9.1/lib/mail/parsers/envelope_from_parser.rb +3678 -0
- data/mail-2.9.1/lib/mail/parsers/envelope_from_parser.rl +93 -0
- data/mail-2.9.1/lib/mail/parsers/message_ids_parser.rb +5164 -0
- data/mail-2.9.1/lib/mail/parsers/message_ids_parser.rl +97 -0
- data/mail-2.9.1/lib/mail/parsers/mime_version_parser.rb +516 -0
- data/mail-2.9.1/lib/mail/parsers/mime_version_parser.rl +72 -0
- data/mail-2.9.1/lib/mail/parsers/phrase_lists_parser.rb +887 -0
- data/mail-2.9.1/lib/mail/parsers/phrase_lists_parser.rl +94 -0
- data/mail-2.9.1/lib/mail/parsers/received_parser.rb +8785 -0
- data/mail-2.9.1/lib/mail/parsers/received_parser.rl +95 -0
- data/mail-2.9.1/lib/mail/parsers/rfc2045_content_transfer_encoding.rl +13 -0
- data/mail-2.9.1/lib/mail/parsers/rfc2045_content_type.rl +25 -0
- data/mail-2.9.1/lib/mail/parsers/rfc2045_mime.rl +16 -0
- data/mail-2.9.1/lib/mail/parsers/rfc2183_content_disposition.rl +15 -0
- data/mail-2.9.1/lib/mail/parsers/rfc3629_utf8.rl +19 -0
- data/mail-2.9.1/lib/mail/parsers/rfc5234_abnf_core_rules.rl +22 -0
- data/mail-2.9.1/lib/mail/parsers/rfc5322.rl +74 -0
- data/mail-2.9.1/lib/mail/parsers/rfc5322_address.rl +72 -0
- data/mail-2.9.1/lib/mail/parsers/rfc5322_date_time.rl +37 -0
- data/mail-2.9.1/lib/mail/parsers/rfc5322_lexical_tokens.rl +60 -0
- data/mail-2.9.1/lib/mail/parsers.rb +13 -0
- data/mail-2.9.1/lib/mail/part.rb +119 -0
- data/mail-2.9.1/lib/mail/parts_list.rb +131 -0
- data/mail-2.9.1/lib/mail/smtp_envelope.rb +57 -0
- data/mail-2.9.1/lib/mail/utilities.rb +576 -0
- data/mail-2.9.1/lib/mail/values/unicode_tables.dat +0 -0
- data/mail-2.9.1/lib/mail/version.rb +17 -0
- data/mail-2.9.1/lib/mail/yaml.rb +30 -0
- data/mail-2.9.1/lib/mail.rb +65 -0
- data/nano-fast-tool.gemspec +12 -0
- metadata +177 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
require 'mail/encodings/7bit'
|
|
4
|
+
|
|
5
|
+
module Mail
|
|
6
|
+
module Encodings
|
|
7
|
+
class QuotedPrintable < SevenBit
|
|
8
|
+
NAME='quoted-printable'
|
|
9
|
+
|
|
10
|
+
PRIORITY = 2
|
|
11
|
+
|
|
12
|
+
def self.can_encode?(enc)
|
|
13
|
+
EightBit.can_encode? enc
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Decode the string from Quoted-Printable. Cope with hard line breaks
|
|
17
|
+
# that were incorrectly encoded as hex instead of literal CRLF.
|
|
18
|
+
def self.decode(str)
|
|
19
|
+
::Mail::Utilities.to_lf ::Mail::Utilities.to_crlf(str).gsub(/(?:=0D=0A|=0D|=0A)\r\n/, "\r\n").unpack1("M*")
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.encode(str)
|
|
23
|
+
::Mail::Utilities.to_crlf [::Mail::Utilities.to_lf(str)].pack("M")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.cost(str)
|
|
27
|
+
# These bytes probably do not need encoding
|
|
28
|
+
c = str.count("\x9\xA\xD\x20-\x3C\x3E-\x7E")
|
|
29
|
+
# Everything else turns into =XX where XX is a
|
|
30
|
+
# two digit hex number (taking 3 bytes)
|
|
31
|
+
total = (str.bytesize - c)*3 + c
|
|
32
|
+
total.to_f/str.bytesize
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# QP inserts newlines automatically and cannot violate the SMTP spec.
|
|
36
|
+
def self.compatible_input?(str)
|
|
37
|
+
true
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
Encodings.register(NAME, self)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
module Mail
|
|
4
|
+
module Encodings
|
|
5
|
+
class TransferEncoding
|
|
6
|
+
NAME = ''
|
|
7
|
+
|
|
8
|
+
PRIORITY = -1
|
|
9
|
+
|
|
10
|
+
# And encoding's superclass can always transport it since the
|
|
11
|
+
# class hierarchy is arranged e.g. Base64 < 7bit < 8bit < Binary.
|
|
12
|
+
def self.can_transport?(enc)
|
|
13
|
+
enc && enc <= self
|
|
14
|
+
end
|
|
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.
|
|
19
|
+
def self.can_encode?(enc)
|
|
20
|
+
can_transport? enc
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.cost(str)
|
|
24
|
+
raise "Unimplemented"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.compatible_input?(str)
|
|
28
|
+
true
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def self.to_s
|
|
32
|
+
self::NAME
|
|
33
|
+
end
|
|
34
|
+
|
|
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
|
|
41
|
+
else
|
|
42
|
+
renegotiate(message_encoding, source_encoding, str, allowed_encodings)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
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
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
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/, '').unpack1('u')
|
|
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
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Mail
|
|
5
|
+
# Raised when attempting to decode an unknown encoding type
|
|
6
|
+
class UnknownEncodingType < StandardError #:nodoc:
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
module Encodings
|
|
10
|
+
include Mail::Constants
|
|
11
|
+
extend Mail::Utilities
|
|
12
|
+
|
|
13
|
+
@transfer_encodings = {}
|
|
14
|
+
|
|
15
|
+
# Register transfer encoding
|
|
16
|
+
#
|
|
17
|
+
# Example
|
|
18
|
+
#
|
|
19
|
+
# Encodings.register "base64", Mail::Encodings::Base64
|
|
20
|
+
def Encodings.register(name, cls)
|
|
21
|
+
@transfer_encodings[get_name(name)] = cls
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Is the encoding we want defined?
|
|
25
|
+
#
|
|
26
|
+
# Example:
|
|
27
|
+
#
|
|
28
|
+
# Encodings.defined?(:base64) #=> true
|
|
29
|
+
def Encodings.defined?(name)
|
|
30
|
+
@transfer_encodings.include? get_name(name)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Gets a defined encoding type, QuotedPrintable or Base64 for now.
|
|
34
|
+
#
|
|
35
|
+
# Each encoding needs to be defined as a Mail::Encodings::ClassName for
|
|
36
|
+
# this to work, allows us to add other encodings in the future.
|
|
37
|
+
#
|
|
38
|
+
# Example:
|
|
39
|
+
#
|
|
40
|
+
# Encodings.get_encoding(:base64) #=> Mail::Encodings::Base64
|
|
41
|
+
def Encodings.get_encoding(name)
|
|
42
|
+
@transfer_encodings[get_name(name)]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def Encodings.get_all
|
|
46
|
+
@transfer_encodings.values
|
|
47
|
+
end
|
|
48
|
+
|
|
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
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Encodes a parameter value using URI Escaping, note the language field 'en' can
|
|
62
|
+
# be set using Mail::Configuration, like so:
|
|
63
|
+
#
|
|
64
|
+
# Mail.defaults do
|
|
65
|
+
# param_encode_language 'jp'
|
|
66
|
+
# end
|
|
67
|
+
#
|
|
68
|
+
# The character set used for encoding will be the encoding on the string passed in.
|
|
69
|
+
#
|
|
70
|
+
# Example:
|
|
71
|
+
#
|
|
72
|
+
# Mail::Encodings.param_encode("This is fun") #=> "us-ascii'en'This%20is%20fun"
|
|
73
|
+
def Encodings.param_encode(str)
|
|
74
|
+
case
|
|
75
|
+
when str.ascii_only? && TOKEN_UNSAFE.match?(str)
|
|
76
|
+
%Q{"#{str}"}
|
|
77
|
+
when str.ascii_only?
|
|
78
|
+
str
|
|
79
|
+
else
|
|
80
|
+
Utilities.param_encode(str)
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Decodes a parameter value using URI Escaping.
|
|
85
|
+
#
|
|
86
|
+
# Example:
|
|
87
|
+
#
|
|
88
|
+
# Mail::Encodings.param_decode("This%20is%20fun", 'us-ascii') #=> "This is fun"
|
|
89
|
+
#
|
|
90
|
+
# str = Mail::Encodings.param_decode("This%20is%20fun", 'iso-8559-1')
|
|
91
|
+
# str.encoding #=> 'ISO-8859-1' ## Only on Ruby 1.9
|
|
92
|
+
# str #=> "This is fun"
|
|
93
|
+
def Encodings.param_decode(str, encoding)
|
|
94
|
+
Utilities.param_decode(str, encoding)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Decodes or encodes a string as needed for either Base64 or QP encoding types in
|
|
98
|
+
# the =?<encoding>?[QB]?<string>?=" format.
|
|
99
|
+
#
|
|
100
|
+
# The output type needs to be :decode to decode the input string or :encode to
|
|
101
|
+
# encode the input string. The character set used for encoding will be the
|
|
102
|
+
# encoding on the string passed in.
|
|
103
|
+
#
|
|
104
|
+
# On encoding, will only send out Base64 encoded strings.
|
|
105
|
+
def Encodings.decode_encode(str, output_type)
|
|
106
|
+
case
|
|
107
|
+
when output_type == :decode
|
|
108
|
+
Encodings.value_decode(str)
|
|
109
|
+
else
|
|
110
|
+
if str.ascii_only?
|
|
111
|
+
str
|
|
112
|
+
else
|
|
113
|
+
Encodings.b_value_encode(str, str.encoding)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Decodes a given string as Base64 or Quoted Printable, depending on what
|
|
119
|
+
# type it is.
|
|
120
|
+
#
|
|
121
|
+
# String has to be of the format =?<encoding>?[QB]?<string>?=
|
|
122
|
+
def Encodings.value_decode(str)
|
|
123
|
+
# Optimization: If there's no encoded-words in the string, just return it
|
|
124
|
+
return str unless ENCODED_VALUE.match?(str)
|
|
125
|
+
|
|
126
|
+
lines = collapse_adjacent_encodings(str)
|
|
127
|
+
|
|
128
|
+
# Split on white-space boundaries with capture, so we capture the white-space as well
|
|
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)
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end.join("")
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Takes an encoded string of the format =?<encoding>?[QB]?<string>?=
|
|
140
|
+
def Encodings.unquote_and_convert_to(str, to_encoding)
|
|
141
|
+
output = value_decode( str ).to_s # output is already converted to UTF-8
|
|
142
|
+
|
|
143
|
+
if 'utf8' == to_encoding.to_s.downcase.gsub("-", "")
|
|
144
|
+
output
|
|
145
|
+
elsif to_encoding
|
|
146
|
+
begin
|
|
147
|
+
output.encode(to_encoding)
|
|
148
|
+
rescue Errno::EINVAL
|
|
149
|
+
# the 'from' parameter specifies a charset other than what the text
|
|
150
|
+
# actually is...not much we can do in this case but just return the
|
|
151
|
+
# unconverted text.
|
|
152
|
+
#
|
|
153
|
+
# Ditto if either parameter represents an unknown charset, like
|
|
154
|
+
# X-UNKNOWN.
|
|
155
|
+
output
|
|
156
|
+
end
|
|
157
|
+
else
|
|
158
|
+
output
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def Encodings.address_encode(address, charset = 'utf-8')
|
|
163
|
+
if address.is_a?(Array)
|
|
164
|
+
address.compact.map { |a| Encodings.address_encode(a, charset) }.join(", ")
|
|
165
|
+
elsif address
|
|
166
|
+
encode_non_usascii(address, charset)
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def Encodings.encode_non_usascii(address, charset)
|
|
171
|
+
return address if address.ascii_only? or charset.nil?
|
|
172
|
+
|
|
173
|
+
# Encode all strings embedded inside of quotes
|
|
174
|
+
address = address.gsub(/("[^"]*[^\/]")/) { |s| Encodings.b_value_encode(unquote(s), charset) }
|
|
175
|
+
|
|
176
|
+
# Then loop through all remaining items and encode as needed
|
|
177
|
+
tokens = address.split(/\s/)
|
|
178
|
+
|
|
179
|
+
map_with_index(tokens) do |word, i|
|
|
180
|
+
if word.ascii_only?
|
|
181
|
+
word
|
|
182
|
+
else
|
|
183
|
+
previous_non_ascii = i>0 && tokens[i-1] && !tokens[i-1].ascii_only?
|
|
184
|
+
if previous_non_ascii #why are we adding an extra space here?
|
|
185
|
+
word = " #{word}"
|
|
186
|
+
end
|
|
187
|
+
Encodings.b_value_encode(word, charset)
|
|
188
|
+
end
|
|
189
|
+
end.join(' ')
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# Encode a string with Base64 Encoding and returns it ready to be inserted
|
|
193
|
+
# as a value for a field, that is, in the =?<charset>?B?<string>?= format
|
|
194
|
+
#
|
|
195
|
+
# Example:
|
|
196
|
+
#
|
|
197
|
+
# Encodings.b_value_encode('This is あ string', 'UTF-8')
|
|
198
|
+
# #=> "=?UTF-8?B?VGhpcyBpcyDjgYIgc3RyaW5n?="
|
|
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
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# Encode a string with Quoted-Printable Encoding and returns it ready to be inserted
|
|
211
|
+
# as a value for a field, that is, in the =?<charset>?Q?<string>?= format
|
|
212
|
+
#
|
|
213
|
+
# Example:
|
|
214
|
+
#
|
|
215
|
+
# Encodings.q_value_encode('This is あ string', 'UTF-8')
|
|
216
|
+
# #=> "=?UTF-8?Q?This_is_=E3=81=82_string?="
|
|
217
|
+
def Encodings.q_value_encode(encoded_str, encoding = nil)
|
|
218
|
+
return encoded_str if encoded_str.to_s.ascii_only?
|
|
219
|
+
string, encoding = Utilities.q_value_encode(encoded_str, encoding)
|
|
220
|
+
string.gsub!("=\r\n", '') # We already have limited the string to the length we want
|
|
221
|
+
map_lines(string) do |str|
|
|
222
|
+
"=?#{encoding}?Q?#{str.chomp.gsub(/ /, '_')}?="
|
|
223
|
+
end.join(" ")
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
private
|
|
227
|
+
|
|
228
|
+
# Decodes a Base64 string from the "=?UTF-8?B?VGhpcyBpcyDjgYIgc3RyaW5n?=" format
|
|
229
|
+
#
|
|
230
|
+
# Example:
|
|
231
|
+
#
|
|
232
|
+
# Encodings.b_value_decode("=?UTF-8?B?VGhpcyBpcyDjgYIgc3RyaW5n?=")
|
|
233
|
+
# #=> 'This is あ string'
|
|
234
|
+
def Encodings.b_value_decode(str)
|
|
235
|
+
Utilities.b_value_decode(str)
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
# Decodes a Quoted-Printable string from the "=?UTF-8?Q?This_is_=E3=81=82_string?=" format
|
|
239
|
+
#
|
|
240
|
+
# Example:
|
|
241
|
+
#
|
|
242
|
+
# Encodings.q_value_decode("=?UTF-8?Q?This_is_=E3=81=82_string?=")
|
|
243
|
+
# #=> 'This is あ string'
|
|
244
|
+
def Encodings.q_value_decode(str)
|
|
245
|
+
Utilities.q_value_decode(str)
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# Gets the encoding type (Q or B) from the string.
|
|
249
|
+
def Encodings.value_encoding_from_string(str)
|
|
250
|
+
str[ENCODED_VALUE, 1]
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
# Split header line into proper encoded and unencoded parts.
|
|
254
|
+
#
|
|
255
|
+
# String has to be of the format =?<encoding>?[QB]?<string>?=
|
|
256
|
+
#
|
|
257
|
+
# Omit unencoded space after an encoded-word.
|
|
258
|
+
def Encodings.collapse_adjacent_encodings(str)
|
|
259
|
+
results = []
|
|
260
|
+
last_encoded = nil # Track whether to preserve or drop whitespace
|
|
261
|
+
|
|
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
|
|
268
|
+
|
|
269
|
+
results << encoded
|
|
270
|
+
else
|
|
271
|
+
results << unencoded
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
results
|
|
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
|
|
313
|
+
end
|
|
314
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
#
|
|
4
|
+
# = Mail Envelope
|
|
5
|
+
#
|
|
6
|
+
# The Envelope class provides a field for the first line in an
|
|
7
|
+
# mbox file, that looks like "From mikel@test.lindsaar.net DATETIME"
|
|
8
|
+
#
|
|
9
|
+
# This envelope class reads that line, and turns it into an
|
|
10
|
+
# Envelope.from and Envelope.date for your use.
|
|
11
|
+
|
|
12
|
+
module Mail
|
|
13
|
+
class Envelope < NamedStructuredField
|
|
14
|
+
NAME = 'Envelope-From'
|
|
15
|
+
|
|
16
|
+
def element
|
|
17
|
+
@element ||= Mail::EnvelopeFromElement.new(value)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def from
|
|
21
|
+
element.address
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def date
|
|
25
|
+
element.date_time
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|