dball-mail 2.2.9.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.
- data/CHANGELOG.rdoc +459 -0
- data/README.rdoc +582 -0
- data/Rakefile +66 -0
- data/TODO.rdoc +9 -0
- data/lib/VERSION +4 -0
- data/lib/mail/attachments_list.rb +105 -0
- data/lib/mail/body.rb +286 -0
- data/lib/mail/configuration.rb +71 -0
- data/lib/mail/core_extensions/nil.rb +11 -0
- data/lib/mail/core_extensions/string.rb +27 -0
- data/lib/mail/elements/address.rb +306 -0
- data/lib/mail/elements/address_list.rb +74 -0
- data/lib/mail/elements/content_disposition_element.rb +30 -0
- data/lib/mail/elements/content_location_element.rb +25 -0
- data/lib/mail/elements/content_transfer_encoding_element.rb +24 -0
- data/lib/mail/elements/content_type_element.rb +35 -0
- data/lib/mail/elements/date_time_element.rb +26 -0
- data/lib/mail/elements/envelope_from_element.rb +34 -0
- data/lib/mail/elements/message_ids_element.rb +29 -0
- data/lib/mail/elements/mime_version_element.rb +26 -0
- data/lib/mail/elements/phrase_list.rb +21 -0
- data/lib/mail/elements/received_element.rb +30 -0
- data/lib/mail/elements.rb +14 -0
- data/lib/mail/encodings/7bit.rb +31 -0
- data/lib/mail/encodings/8bit.rb +31 -0
- data/lib/mail/encodings/base64.rb +33 -0
- data/lib/mail/encodings/binary.rb +31 -0
- data/lib/mail/encodings/quoted_printable.rb +38 -0
- data/lib/mail/encodings/transfer_encoding.rb +58 -0
- data/lib/mail/encodings.rb +268 -0
- data/lib/mail/envelope.rb +35 -0
- data/lib/mail/field.rb +223 -0
- data/lib/mail/field_list.rb +33 -0
- data/lib/mail/fields/bcc_field.rb +56 -0
- data/lib/mail/fields/cc_field.rb +55 -0
- data/lib/mail/fields/comments_field.rb +41 -0
- data/lib/mail/fields/common/address_container.rb +16 -0
- data/lib/mail/fields/common/common_address.rb +125 -0
- data/lib/mail/fields/common/common_date.rb +42 -0
- data/lib/mail/fields/common/common_field.rb +50 -0
- data/lib/mail/fields/common/common_message_id.rb +44 -0
- data/lib/mail/fields/common/parameter_hash.rb +58 -0
- data/lib/mail/fields/content_description_field.rb +19 -0
- data/lib/mail/fields/content_disposition_field.rb +69 -0
- data/lib/mail/fields/content_id_field.rb +63 -0
- data/lib/mail/fields/content_location_field.rb +42 -0
- data/lib/mail/fields/content_transfer_encoding_field.rb +50 -0
- data/lib/mail/fields/content_type_field.rb +198 -0
- data/lib/mail/fields/date_field.rb +55 -0
- data/lib/mail/fields/from_field.rb +55 -0
- data/lib/mail/fields/in_reply_to_field.rb +55 -0
- data/lib/mail/fields/keywords_field.rb +44 -0
- data/lib/mail/fields/message_id_field.rb +83 -0
- data/lib/mail/fields/mime_version_field.rb +53 -0
- data/lib/mail/fields/optional_field.rb +13 -0
- data/lib/mail/fields/received_field.rb +67 -0
- data/lib/mail/fields/references_field.rb +55 -0
- data/lib/mail/fields/reply_to_field.rb +55 -0
- data/lib/mail/fields/resent_bcc_field.rb +55 -0
- data/lib/mail/fields/resent_cc_field.rb +55 -0
- data/lib/mail/fields/resent_date_field.rb +35 -0
- data/lib/mail/fields/resent_from_field.rb +55 -0
- data/lib/mail/fields/resent_message_id_field.rb +34 -0
- data/lib/mail/fields/resent_sender_field.rb +62 -0
- data/lib/mail/fields/resent_to_field.rb +55 -0
- data/lib/mail/fields/return_path_field.rb +64 -0
- data/lib/mail/fields/sender_field.rb +67 -0
- data/lib/mail/fields/structured_field.rb +51 -0
- data/lib/mail/fields/subject_field.rb +16 -0
- data/lib/mail/fields/to_field.rb +55 -0
- data/lib/mail/fields/unstructured_field.rb +179 -0
- data/lib/mail/fields.rb +35 -0
- data/lib/mail/header.rb +264 -0
- data/lib/mail/mail.rb +255 -0
- data/lib/mail/message.rb +1972 -0
- data/lib/mail/network/delivery_methods/file_delivery.rb +40 -0
- data/lib/mail/network/delivery_methods/sendmail.rb +62 -0
- data/lib/mail/network/delivery_methods/smtp.rb +136 -0
- data/lib/mail/network/delivery_methods/test_mailer.rb +40 -0
- data/lib/mail/network/retriever_methods/imap.rb +213 -0
- data/lib/mail/network/retriever_methods/pop3.rb +194 -0
- data/lib/mail/network/retriever_methods/test_retriever.rb +31 -0
- data/lib/mail/network.rb +10 -0
- data/lib/mail/parsers/address_lists.rb +64 -0
- data/lib/mail/parsers/address_lists.treetop +19 -0
- data/lib/mail/parsers/content_disposition.rb +535 -0
- data/lib/mail/parsers/content_disposition.treetop +46 -0
- data/lib/mail/parsers/content_location.rb +139 -0
- data/lib/mail/parsers/content_location.treetop +20 -0
- data/lib/mail/parsers/content_transfer_encoding.rb +162 -0
- data/lib/mail/parsers/content_transfer_encoding.treetop +20 -0
- data/lib/mail/parsers/content_type.rb +967 -0
- data/lib/mail/parsers/content_type.treetop +68 -0
- data/lib/mail/parsers/date_time.rb +114 -0
- data/lib/mail/parsers/date_time.treetop +11 -0
- data/lib/mail/parsers/envelope_from.rb +194 -0
- data/lib/mail/parsers/envelope_from.treetop +32 -0
- data/lib/mail/parsers/message_ids.rb +45 -0
- data/lib/mail/parsers/message_ids.treetop +15 -0
- data/lib/mail/parsers/mime_version.rb +144 -0
- data/lib/mail/parsers/mime_version.treetop +19 -0
- data/lib/mail/parsers/phrase_lists.rb +45 -0
- data/lib/mail/parsers/phrase_lists.treetop +15 -0
- data/lib/mail/parsers/received.rb +71 -0
- data/lib/mail/parsers/received.treetop +11 -0
- data/lib/mail/parsers/rfc2045.rb +464 -0
- data/lib/mail/parsers/rfc2045.treetop +36 -0
- data/lib/mail/parsers/rfc2822.rb +5318 -0
- data/lib/mail/parsers/rfc2822.treetop +410 -0
- data/lib/mail/parsers/rfc2822_obsolete.rb +3757 -0
- data/lib/mail/parsers/rfc2822_obsolete.treetop +241 -0
- data/lib/mail/part.rb +116 -0
- data/lib/mail/parts_list.rb +43 -0
- data/lib/mail/patterns.rb +34 -0
- data/lib/mail/utilities.rb +211 -0
- data/lib/mail/version.rb +24 -0
- data/lib/mail/version_specific/ruby_1_8.rb +97 -0
- data/lib/mail/version_specific/ruby_1_9.rb +87 -0
- data/lib/mail.rb +80 -0
- data/lib/tasks/corpus.rake +125 -0
- data/lib/tasks/treetop.rake +10 -0
- metadata +255 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
require 'mail/encodings/8bit'
|
|
3
|
+
|
|
4
|
+
module Mail
|
|
5
|
+
module Encodings
|
|
6
|
+
class SevenBit < EightBit
|
|
7
|
+
NAME = '7bit'
|
|
8
|
+
|
|
9
|
+
PRIORITY = 1
|
|
10
|
+
|
|
11
|
+
# 7bit and 8bit operate the same
|
|
12
|
+
|
|
13
|
+
# Decode the string
|
|
14
|
+
def self.decode(str)
|
|
15
|
+
super
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Encode the string
|
|
19
|
+
def self.encode(str)
|
|
20
|
+
super
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Idenity encodings have a fixed cost, 1 byte out per 1 byte in
|
|
24
|
+
def self.cost(str)
|
|
25
|
+
super
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
Encodings.register(NAME, self)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
require 'mail/encodings/binary'
|
|
3
|
+
|
|
4
|
+
module Mail
|
|
5
|
+
module Encodings
|
|
6
|
+
class EightBit < Binary
|
|
7
|
+
NAME = '8bit'
|
|
8
|
+
|
|
9
|
+
PRIORITY = 4
|
|
10
|
+
|
|
11
|
+
# 8bit is an identiy encoding, meaning nothing to do
|
|
12
|
+
|
|
13
|
+
# Decode the string
|
|
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
|
|
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)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
require 'mail/encodings/7bit'
|
|
3
|
+
|
|
4
|
+
module Mail
|
|
5
|
+
module Encodings
|
|
6
|
+
class Base64 < SevenBit
|
|
7
|
+
NAME = 'base64'
|
|
8
|
+
|
|
9
|
+
PRIORITY = 3
|
|
10
|
+
|
|
11
|
+
def self.can_encode?(enc)
|
|
12
|
+
true
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Decode the string from Base64
|
|
16
|
+
def self.decode(str)
|
|
17
|
+
RubyVer.decode_base64( str )
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Encode the string to Base64
|
|
21
|
+
def self.encode(str)
|
|
22
|
+
RubyVer.encode_base64( str ).to_crlf
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Base64 has a fixed cost, 4 bytes out per 3 bytes in
|
|
26
|
+
def self.cost(str)
|
|
27
|
+
4.0/3
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
Encodings.register(NAME, self)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
require 'mail/encodings/transfer_encoding'
|
|
3
|
+
|
|
4
|
+
module Mail
|
|
5
|
+
module Encodings
|
|
6
|
+
class Binary < TransferEncoding
|
|
7
|
+
NAME = 'binary'
|
|
8
|
+
|
|
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)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
require 'mail/encodings/7bit'
|
|
3
|
+
|
|
4
|
+
module Mail
|
|
5
|
+
module Encodings
|
|
6
|
+
class QuotedPrintable < SevenBit
|
|
7
|
+
NAME='quoted-printable'
|
|
8
|
+
|
|
9
|
+
PRIORITY = 2
|
|
10
|
+
|
|
11
|
+
def self.can_encode?(str)
|
|
12
|
+
EightBit.can_encode? str
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Decode the string from Quoted-Printable
|
|
16
|
+
def self.decode(str)
|
|
17
|
+
str.unpack("M*").first
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.encode(str)
|
|
21
|
+
[str].pack("M").gsub(/\n/, "\r\n")
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.cost(str)
|
|
25
|
+
# These bytes probably do not need encoding
|
|
26
|
+
c = str.count("\x9\xA\xD\x20-\x3C\x3E-\x7E")
|
|
27
|
+
# Everything else turns into =XX where XX is a
|
|
28
|
+
# two digit hex number (taking 3 bytes)
|
|
29
|
+
total = (str.bytesize - c)*3 + c
|
|
30
|
+
total.to_f/str.bytesize
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
Encodings.register(NAME, self)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module Mail
|
|
3
|
+
module Encodings
|
|
4
|
+
class TransferEncoding
|
|
5
|
+
NAME = ''
|
|
6
|
+
|
|
7
|
+
PRIORITY = -1
|
|
8
|
+
|
|
9
|
+
def self.can_transport?(enc)
|
|
10
|
+
enc = Encodings.get_name(enc)
|
|
11
|
+
if Encodings.defined? enc
|
|
12
|
+
Encodings.get_encoding(enc).new.is_a? self
|
|
13
|
+
else
|
|
14
|
+
false
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.can_encode?(enc)
|
|
19
|
+
can_transport? enc
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.cost(str)
|
|
23
|
+
raise "Unimplemented"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.to_s
|
|
27
|
+
self::NAME
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.get_best_compatible(source_encoding, str)
|
|
31
|
+
if self.can_transport? source_encoding then
|
|
32
|
+
source_encoding
|
|
33
|
+
else
|
|
34
|
+
choices = []
|
|
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
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def to_s
|
|
54
|
+
self.class.to_s
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module Mail
|
|
4
|
+
# Raised when attempting to decode an unknown encoding type
|
|
5
|
+
class UnknownEncodingType < StandardError #:nodoc:
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
module Encodings
|
|
9
|
+
|
|
10
|
+
include Mail::Patterns
|
|
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?( str )
|
|
30
|
+
@transfer_encodings.include? get_name(str)
|
|
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( str )
|
|
42
|
+
@transfer_encodings[get_name(str)]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def Encodings.get_all
|
|
46
|
+
@transfer_encodings.values
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def Encodings.get_name(enc)
|
|
50
|
+
enc = enc.to_s.gsub("-", "_").downcase
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Encodes a parameter value using URI Escaping, note the language field 'en' can
|
|
54
|
+
# be set using Mail::Configuration, like so:
|
|
55
|
+
#
|
|
56
|
+
# Mail.defaults.do
|
|
57
|
+
# param_encode_language 'jp'
|
|
58
|
+
# end
|
|
59
|
+
#
|
|
60
|
+
# The character set used for encoding will either be the value of $KCODE for
|
|
61
|
+
# Ruby < 1.9 or the encoding on the string passed in.
|
|
62
|
+
#
|
|
63
|
+
# Example:
|
|
64
|
+
#
|
|
65
|
+
# Mail::Encodings.param_encode("This is fun") #=> "us-ascii'en'This%20is%20fun"
|
|
66
|
+
def Encodings.param_encode(str)
|
|
67
|
+
case
|
|
68
|
+
when str.ascii_only? && str =~ TOKEN_UNSAFE
|
|
69
|
+
%Q{"#{str}"}
|
|
70
|
+
when str.ascii_only?
|
|
71
|
+
str
|
|
72
|
+
else
|
|
73
|
+
RubyVer.param_encode(str)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Decodes a parameter value using URI Escaping.
|
|
78
|
+
#
|
|
79
|
+
# Example:
|
|
80
|
+
#
|
|
81
|
+
# Mail::Encodings.param_decode("This%20is%20fun", 'us-ascii') #=> "This is fun"
|
|
82
|
+
#
|
|
83
|
+
# str = Mail::Encodings.param_decode("This%20is%20fun", 'iso-8559-1')
|
|
84
|
+
# str.encoding #=> 'ISO-8859-1' ## Only on Ruby 1.9
|
|
85
|
+
# str #=> "This is fun"
|
|
86
|
+
def Encodings.param_decode(str, encoding)
|
|
87
|
+
RubyVer.param_decode(str, encoding)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Decodes or encodes a string as needed for either Base64 or QP encoding types in
|
|
91
|
+
# the =?<encoding>?[QB]?<string>?=" format.
|
|
92
|
+
#
|
|
93
|
+
# 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 either be
|
|
95
|
+
# the value of $KCODE for Ruby < 1.9 or the encoding on the string passed in.
|
|
96
|
+
#
|
|
97
|
+
# On encoding, will only send out Base64 encoded strings.
|
|
98
|
+
def Encodings.decode_encode(str, output_type)
|
|
99
|
+
case
|
|
100
|
+
when output_type == :decode
|
|
101
|
+
Encodings.value_decode(str)
|
|
102
|
+
else
|
|
103
|
+
if str.ascii_only?
|
|
104
|
+
str
|
|
105
|
+
else
|
|
106
|
+
Encodings.b_value_encode(str, find_encoding(str))
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Decodes a given string as Base64 or Quoted Printable, depending on what
|
|
112
|
+
# type it is.
|
|
113
|
+
#
|
|
114
|
+
# String has to be of the format =?<encoding>?[QB]?<string>?=
|
|
115
|
+
def Encodings.value_decode(str)
|
|
116
|
+
# Optimization: If there's no encoded-words in the string, just return it
|
|
117
|
+
return str unless str.index("=?")
|
|
118
|
+
|
|
119
|
+
str = str.gsub(/\?=(\s*)=\?/, '?==?') # Remove whitespaces between 'encoded-word's
|
|
120
|
+
|
|
121
|
+
# Split on white-space boundaries with capture, so we capture the white-space as well
|
|
122
|
+
str.split(/([ \t])/).map do |text|
|
|
123
|
+
if text.index('=?') != 0
|
|
124
|
+
text
|
|
125
|
+
else
|
|
126
|
+
# Join QP encoded-words that are adjacent to avoid decoding partial chars
|
|
127
|
+
text.gsub!(/\?\=\=\?.+?\?[Qq]\?/m, '') if text =~ /\?==\?/
|
|
128
|
+
|
|
129
|
+
# Separate encoded-words with a space, so we can treat them one by one
|
|
130
|
+
text.gsub!(/\?\=\=\?/, '?= =?')
|
|
131
|
+
|
|
132
|
+
text.split(/ /).map do |word|
|
|
133
|
+
case
|
|
134
|
+
when word.to_str =~ /=\?.+\?[Bb]\?/m
|
|
135
|
+
b_value_decode(word)
|
|
136
|
+
when text.to_str =~ /=\?.+\?[Qq]\?/m
|
|
137
|
+
q_value_decode(word)
|
|
138
|
+
else
|
|
139
|
+
word.to_str
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end.join("")
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Takes an encoded string of the format =?<encoding>?[QB]?<string>?=
|
|
147
|
+
def Encodings.unquote_and_convert_to(str, to_encoding)
|
|
148
|
+
original_encoding, string = split_encoding_from_string( str )
|
|
149
|
+
|
|
150
|
+
output = value_decode( str ).to_s
|
|
151
|
+
|
|
152
|
+
if original_encoding.to_s.downcase.gsub("-", "") == to_encoding.to_s.downcase.gsub("-", "")
|
|
153
|
+
output
|
|
154
|
+
elsif original_encoding && to_encoding
|
|
155
|
+
begin
|
|
156
|
+
require 'iconv'
|
|
157
|
+
Iconv.iconv(to_encoding, original_encoding, output).first
|
|
158
|
+
rescue Iconv::IllegalSequence, Iconv::InvalidEncoding, Errno::EINVAL
|
|
159
|
+
# the 'from' parameter specifies a charset other than what the text
|
|
160
|
+
# actually is...not much we can do in this case but just return the
|
|
161
|
+
# unconverted text.
|
|
162
|
+
#
|
|
163
|
+
# Ditto if either parameter represents an unknown charset, like
|
|
164
|
+
# X-UNKNOWN.
|
|
165
|
+
output
|
|
166
|
+
end
|
|
167
|
+
else
|
|
168
|
+
output
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def Encodings.address_encode(address, charset = 'utf-8')
|
|
173
|
+
if address.is_a?(Array)
|
|
174
|
+
# loop back through for each element
|
|
175
|
+
address.map { |a| Encodings.address_encode(a, charset) }.join(", ")
|
|
176
|
+
else
|
|
177
|
+
# find any word boundary that is not ascii and encode it
|
|
178
|
+
encode_non_usascii(address, charset)
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def Encodings.encode_non_usascii(address, charset)
|
|
183
|
+
return address if address.ascii_only?
|
|
184
|
+
us_ascii = %Q{\x00-\x7f}
|
|
185
|
+
# Encode any non usascii strings embedded inside of quotes
|
|
186
|
+
address.gsub!(/(".*?[^#{us_ascii}].+?")/) { |s| Encodings.b_value_encode(unquote(s), charset) }
|
|
187
|
+
# Then loop through all remaining items and encode as needed
|
|
188
|
+
tokens = address.split(/\s/)
|
|
189
|
+
map_with_index(tokens) do |word, i|
|
|
190
|
+
if word.ascii_only?
|
|
191
|
+
word
|
|
192
|
+
else
|
|
193
|
+
previous_non_ascii = tokens[i-1] && !tokens[i-1].ascii_only?
|
|
194
|
+
if previous_non_ascii
|
|
195
|
+
word = " #{word}"
|
|
196
|
+
end
|
|
197
|
+
Encodings.b_value_encode(word, charset)
|
|
198
|
+
end
|
|
199
|
+
end.join(' ')
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# Encode a string with Base64 Encoding and returns it ready to be inserted
|
|
203
|
+
# as a value for a field, that is, in the =?<charset>?B?<string>?= format
|
|
204
|
+
#
|
|
205
|
+
# Example:
|
|
206
|
+
#
|
|
207
|
+
# Encodings.b_value_encode('This is あ string', 'UTF-8')
|
|
208
|
+
# #=> "=?UTF-8?B?VGhpcyBpcyDjgYIgc3RyaW5n?="
|
|
209
|
+
def Encodings.b_value_encode(str, encoding = nil)
|
|
210
|
+
return str if str.to_s.ascii_only?
|
|
211
|
+
string, encoding = RubyVer.b_value_encode(str, encoding)
|
|
212
|
+
map_lines(string) do |str|
|
|
213
|
+
"=?#{encoding}?B?#{str.chomp}?="
|
|
214
|
+
end.join(" ")
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# Encode a string with Quoted-Printable Encoding and returns it ready to be inserted
|
|
218
|
+
# as a value for a field, that is, in the =?<charset>?Q?<string>?= format
|
|
219
|
+
#
|
|
220
|
+
# Example:
|
|
221
|
+
#
|
|
222
|
+
# Encodings.q_value_encode('This is あ string', 'UTF-8')
|
|
223
|
+
# #=> "=?UTF-8?Q?This_is_=E3=81=82_string?="
|
|
224
|
+
def Encodings.q_value_encode(str, encoding = nil)
|
|
225
|
+
return str if str.to_s.ascii_only?
|
|
226
|
+
string, encoding = RubyVer.q_value_encode(str, encoding)
|
|
227
|
+
string.gsub!("=\r\n", '') # We already have limited the string to the length we want
|
|
228
|
+
map_lines(string) do |str|
|
|
229
|
+
"=?#{encoding}?Q?#{str.chomp.gsub(/ /, '_')}?="
|
|
230
|
+
end.join(" ")
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
private
|
|
234
|
+
|
|
235
|
+
# Decodes a Base64 string from the "=?UTF-8?B?VGhpcyBpcyDjgYIgc3RyaW5n?=" format
|
|
236
|
+
#
|
|
237
|
+
# Example:
|
|
238
|
+
#
|
|
239
|
+
# Encodings.b_value_encode("=?UTF-8?B?VGhpcyBpcyDjgYIgc3RyaW5n?=")
|
|
240
|
+
# #=> 'This is あ string'
|
|
241
|
+
def Encodings.b_value_decode(str)
|
|
242
|
+
RubyVer.b_value_decode(str)
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
# Decodes a Quoted-Printable string from the "=?UTF-8?Q?This_is_=E3=81=82_string?=" format
|
|
246
|
+
#
|
|
247
|
+
# Example:
|
|
248
|
+
#
|
|
249
|
+
# Encodings.b_value_encode("=?UTF-8?Q?This_is_=E3=81=82_string?=")
|
|
250
|
+
# #=> 'This is あ string'
|
|
251
|
+
def Encodings.q_value_decode(str)
|
|
252
|
+
RubyVer.q_value_decode(str).gsub(/_/, ' ')
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def Encodings.split_encoding_from_string( str )
|
|
256
|
+
match = str.match(/\=\?([^?]+)?\?[QB]\?(.+)?\?\=/mi)
|
|
257
|
+
if match
|
|
258
|
+
[match[1], match[2]]
|
|
259
|
+
else
|
|
260
|
+
nil
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
def Encodings.find_encoding(str)
|
|
265
|
+
RUBY_VERSION >= '1.9' ? str.encoding : $KCODE
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
#
|
|
3
|
+
# = Mail Envelope
|
|
4
|
+
#
|
|
5
|
+
# The Envelope class provides a field for the first line in an
|
|
6
|
+
# mbox file, that looks like "From mikel@test.lindsaar.net DATETIME"
|
|
7
|
+
#
|
|
8
|
+
# This envelope class reads that line, and turns it into an
|
|
9
|
+
# Envelope.from and Envelope.date for your use.
|
|
10
|
+
module Mail
|
|
11
|
+
class Envelope < StructuredField
|
|
12
|
+
|
|
13
|
+
def initialize(*args)
|
|
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
|
+
|
|
22
|
+
def element
|
|
23
|
+
@element ||= Mail::EnvelopeFromElement.new(value)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def date
|
|
27
|
+
::DateTime.parse("#{element.date_time}")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def from
|
|
31
|
+
element.address
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
end
|
|
35
|
+
end
|