mail-portertech 2.6.2.edge
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.rdoc +753 -0
- data/CONTRIBUTING.md +60 -0
- data/Dependencies.txt +2 -0
- data/Gemfile +15 -0
- data/MIT-LICENSE +20 -0
- data/README.md +683 -0
- data/Rakefile +29 -0
- data/TODO.rdoc +9 -0
- data/lib/mail.rb +91 -0
- data/lib/mail/attachments_list.rb +104 -0
- data/lib/mail/body.rb +291 -0
- data/lib/mail/check_delivery_params.rb +20 -0
- data/lib/mail/configuration.rb +75 -0
- data/lib/mail/core_extensions/nil.rb +19 -0
- data/lib/mail/core_extensions/object.rb +13 -0
- data/lib/mail/core_extensions/smtp.rb +24 -0
- data/lib/mail/core_extensions/string.rb +43 -0
- data/lib/mail/core_extensions/string/access.rb +145 -0
- data/lib/mail/core_extensions/string/multibyte.rb +78 -0
- data/lib/mail/elements.rb +14 -0
- data/lib/mail/elements/address.rb +270 -0
- data/lib/mail/elements/address_list.rb +51 -0
- data/lib/mail/elements/content_disposition_element.rb +26 -0
- data/lib/mail/elements/content_location_element.rb +21 -0
- data/lib/mail/elements/content_transfer_encoding_element.rb +17 -0
- data/lib/mail/elements/content_type_element.rb +31 -0
- data/lib/mail/elements/date_time_element.rb +22 -0
- data/lib/mail/elements/envelope_from_element.rb +39 -0
- data/lib/mail/elements/message_ids_element.rb +24 -0
- data/lib/mail/elements/mime_version_element.rb +22 -0
- data/lib/mail/elements/phrase_list.rb +16 -0
- data/lib/mail/elements/received_element.rb +26 -0
- data/lib/mail/encodings.rb +304 -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 +39 -0
- data/lib/mail/encodings/transfer_encoding.rb +58 -0
- data/lib/mail/envelope.rb +30 -0
- data/lib/mail/field.rb +247 -0
- data/lib/mail/field_list.rb +33 -0
- data/lib/mail/fields.rb +35 -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 +135 -0
- data/lib/mail/fields/common/common_date.rb +35 -0
- data/lib/mail/fields/common/common_field.rb +57 -0
- data/lib/mail/fields/common/common_message_id.rb +48 -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 +70 -0
- data/lib/mail/fields/content_id_field.rb +62 -0
- data/lib/mail/fields/content_location_field.rb +42 -0
- data/lib/mail/fields/content_transfer_encoding_field.rb +44 -0
- data/lib/mail/fields/content_type_field.rb +201 -0
- data/lib/mail/fields/date_field.rb +57 -0
- data/lib/mail/fields/from_field.rb +55 -0
- data/lib/mail/fields/in_reply_to_field.rb +56 -0
- data/lib/mail/fields/keywords_field.rb +44 -0
- data/lib/mail/fields/message_id_field.rb +82 -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 +75 -0
- data/lib/mail/fields/references_field.rb +56 -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 +65 -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 +204 -0
- data/lib/mail/header.rb +274 -0
- data/lib/mail/indifferent_hash.rb +146 -0
- data/lib/mail/mail.rb +267 -0
- data/lib/mail/matchers/has_sent_mail.rb +157 -0
- data/lib/mail/message.rb +2160 -0
- data/lib/mail/multibyte.rb +42 -0
- data/lib/mail/multibyte/chars.rb +474 -0
- data/lib/mail/multibyte/exceptions.rb +8 -0
- data/lib/mail/multibyte/unicode.rb +400 -0
- data/lib/mail/multibyte/utils.rb +60 -0
- data/lib/mail/network.rb +14 -0
- data/lib/mail/network/delivery_methods/exim.rb +52 -0
- data/lib/mail/network/delivery_methods/file_delivery.rb +45 -0
- data/lib/mail/network/delivery_methods/sendmail.rb +89 -0
- data/lib/mail/network/delivery_methods/smtp.rb +142 -0
- data/lib/mail/network/delivery_methods/smtp_connection.rb +61 -0
- data/lib/mail/network/delivery_methods/test_mailer.rb +44 -0
- data/lib/mail/network/retriever_methods/base.rb +63 -0
- data/lib/mail/network/retriever_methods/imap.rb +173 -0
- data/lib/mail/network/retriever_methods/pop3.rb +140 -0
- data/lib/mail/network/retriever_methods/test_retriever.rb +43 -0
- data/lib/mail/parsers.rb +26 -0
- data/lib/mail/parsers/address_lists_parser.rb +132 -0
- data/lib/mail/parsers/content_disposition_parser.rb +67 -0
- data/lib/mail/parsers/content_location_parser.rb +35 -0
- data/lib/mail/parsers/content_transfer_encoding_parser.rb +33 -0
- data/lib/mail/parsers/content_type_parser.rb +64 -0
- data/lib/mail/parsers/date_time_parser.rb +36 -0
- data/lib/mail/parsers/envelope_from_parser.rb +45 -0
- data/lib/mail/parsers/message_ids_parser.rb +39 -0
- data/lib/mail/parsers/mime_version_parser.rb +41 -0
- data/lib/mail/parsers/phrase_lists_parser.rb +33 -0
- data/lib/mail/parsers/ragel.rb +17 -0
- data/lib/mail/parsers/ragel/common.rl +184 -0
- data/lib/mail/parsers/ragel/date_time.rl +30 -0
- data/lib/mail/parsers/ragel/parser_info.rb +61 -0
- data/lib/mail/parsers/ragel/ruby.rb +39 -0
- data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +14864 -0
- data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +751 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +614 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +447 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +825 -0
- data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +817 -0
- data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +2129 -0
- data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +1570 -0
- data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +440 -0
- data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +564 -0
- data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +51 -0
- data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +5144 -0
- data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +37 -0
- data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +37 -0
- data/lib/mail/parsers/received_parser.rb +47 -0
- data/lib/mail/part.rb +120 -0
- data/lib/mail/parts_list.rb +57 -0
- data/lib/mail/patterns.rb +37 -0
- data/lib/mail/utilities.rb +225 -0
- data/lib/mail/values/unicode_tables.dat +0 -0
- data/lib/mail/version.rb +4 -0
- data/lib/mail/version_specific/ruby_1_8.rb +119 -0
- data/lib/mail/version_specific/ruby_1_9.rb +159 -0
- metadata +276 -0
@@ -0,0 +1,51 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Mail
|
3
|
+
class AddressList # :nodoc:
|
4
|
+
|
5
|
+
# Mail::AddressList is the class that parses To, From and other address fields from
|
6
|
+
# emails passed into Mail.
|
7
|
+
#
|
8
|
+
# AddressList provides a way to query the groups and mailbox lists of the passed in
|
9
|
+
# string.
|
10
|
+
#
|
11
|
+
# It can supply all addresses in an array, or return each address as an address object.
|
12
|
+
#
|
13
|
+
# Mail::AddressList requires a correctly formatted group or mailbox list per RFC2822 or
|
14
|
+
# RFC822. It also handles all obsolete versions in those RFCs.
|
15
|
+
#
|
16
|
+
# list = 'ada@test.lindsaar.net, My Group: mikel@test.lindsaar.net, Bob <bob@test.lindsaar.net>;'
|
17
|
+
# a = AddressList.new(list)
|
18
|
+
# a.addresses #=> [#<Mail::Address:14943130 Address: |ada@test.lindsaar.net...
|
19
|
+
# a.group_names #=> ["My Group"]
|
20
|
+
def initialize(string)
|
21
|
+
@addresses_grouped_by_group = nil
|
22
|
+
@address_list = Parsers::AddressListsParser.new.parse(string)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns a list of address objects from the parsed line
|
26
|
+
def addresses
|
27
|
+
@addresses ||= @address_list.addresses.map do |address_data|
|
28
|
+
Mail::Address.new(address_data)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def addresses_grouped_by_group
|
33
|
+
return @addresses_grouped_by_group if @addresses_grouped_by_group
|
34
|
+
|
35
|
+
@addresses_grouped_by_group = {}
|
36
|
+
|
37
|
+
@address_list.addresses.each do |address_data|
|
38
|
+
if group = address_data.group
|
39
|
+
@addresses_grouped_by_group[group] ||= []
|
40
|
+
@addresses_grouped_by_group[group] << Mail::Address.new(address_data)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
@addresses_grouped_by_group
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns the names as an array of strings of all groups
|
47
|
+
def group_names # :nodoc:
|
48
|
+
@address_list.group_names
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Mail
|
3
|
+
class ContentDispositionElement # :nodoc:
|
4
|
+
|
5
|
+
include Mail::Utilities
|
6
|
+
|
7
|
+
def initialize( string )
|
8
|
+
content_disposition = Mail::Parsers::ContentDispositionParser.new.parse(cleaned(string))
|
9
|
+
@disposition_type = content_disposition.disposition_type
|
10
|
+
@parameters = content_disposition.parameters
|
11
|
+
end
|
12
|
+
|
13
|
+
def disposition_type
|
14
|
+
@disposition_type
|
15
|
+
end
|
16
|
+
|
17
|
+
def parameters
|
18
|
+
@parameters
|
19
|
+
end
|
20
|
+
|
21
|
+
def cleaned(string)
|
22
|
+
string =~ /(.+);\s*$/ ? $1 : string
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Mail
|
3
|
+
class ContentLocationElement # :nodoc:
|
4
|
+
|
5
|
+
include Mail::Utilities
|
6
|
+
|
7
|
+
def initialize( string )
|
8
|
+
content_location = Mail::Parsers::ContentLocationParser.new.parse(string)
|
9
|
+
@location = content_location.location
|
10
|
+
end
|
11
|
+
|
12
|
+
def location
|
13
|
+
@location
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_s(*args)
|
17
|
+
location.to_s
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Mail
|
3
|
+
class ContentTransferEncodingElement
|
4
|
+
|
5
|
+
include Mail::Utilities
|
6
|
+
|
7
|
+
def initialize(string)
|
8
|
+
content_transfer_encoding = Mail::Parsers::ContentTransferEncodingParser.new.parse(string)
|
9
|
+
@encoding = content_transfer_encoding.encoding
|
10
|
+
end
|
11
|
+
|
12
|
+
def encoding
|
13
|
+
@encoding
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Mail
|
3
|
+
class ContentTypeElement # :nodoc:
|
4
|
+
|
5
|
+
include Mail::Utilities
|
6
|
+
|
7
|
+
def initialize( string )
|
8
|
+
content_type = Mail::Parsers::ContentTypeParser.new.parse(cleaned(string))
|
9
|
+
@main_type = content_type.main_type
|
10
|
+
@sub_type = content_type.sub_type
|
11
|
+
@parameters = content_type.parameters
|
12
|
+
end
|
13
|
+
|
14
|
+
def main_type
|
15
|
+
@main_type
|
16
|
+
end
|
17
|
+
|
18
|
+
def sub_type
|
19
|
+
@sub_type
|
20
|
+
end
|
21
|
+
|
22
|
+
def parameters
|
23
|
+
@parameters
|
24
|
+
end
|
25
|
+
|
26
|
+
def cleaned(string)
|
27
|
+
string =~ /(.+);\s*$/ ? $1 : string
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Mail
|
3
|
+
class DateTimeElement # :nodoc:
|
4
|
+
|
5
|
+
include Mail::Utilities
|
6
|
+
|
7
|
+
def initialize( string )
|
8
|
+
date_time = Mail::Parsers::DateTimeParser.new.parse(string)
|
9
|
+
@date_string = date_time.date_string
|
10
|
+
@time_string = date_time.time_string
|
11
|
+
end
|
12
|
+
|
13
|
+
def date_string
|
14
|
+
@date_string
|
15
|
+
end
|
16
|
+
|
17
|
+
def time_string
|
18
|
+
@time_string
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Mail
|
3
|
+
class EnvelopeFromElement
|
4
|
+
|
5
|
+
include Mail::Utilities
|
6
|
+
|
7
|
+
def initialize( string )
|
8
|
+
@envelope_from = Mail::Parsers::EnvelopeFromParser.new.parse(string)
|
9
|
+
@address = @envelope_from.address
|
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
|
19
|
+
end
|
20
|
+
|
21
|
+
# RFC 4155:
|
22
|
+
# a timestamp indicating the UTC date and time when the message
|
23
|
+
# was originally received, conformant with the syntax of the
|
24
|
+
# traditional UNIX 'ctime' output sans timezone (note that the
|
25
|
+
# use of UTC precludes the need for a timezone indicator);
|
26
|
+
def formatted_date_time
|
27
|
+
if @date_time.respond_to?(:ctime)
|
28
|
+
@date_time.ctime
|
29
|
+
else
|
30
|
+
@date_time.strftime '%a %b %e %T %Y'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_s
|
35
|
+
"#{@address} #{formatted_date_time}"
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Mail
|
3
|
+
class MessageIdsElement
|
4
|
+
|
5
|
+
include Mail::Utilities
|
6
|
+
|
7
|
+
def initialize(string)
|
8
|
+
@message_ids = Mail::Parsers::MessageIdsParser.new.parse(string).message_ids.map { |msg_id| clean_msg_id(msg_id) }
|
9
|
+
end
|
10
|
+
|
11
|
+
def message_ids
|
12
|
+
@message_ids
|
13
|
+
end
|
14
|
+
|
15
|
+
def message_id
|
16
|
+
@message_ids.first
|
17
|
+
end
|
18
|
+
|
19
|
+
def clean_msg_id( val )
|
20
|
+
val =~ /.*<(.*)>.*/ ; $1
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Mail
|
3
|
+
class MimeVersionElement
|
4
|
+
|
5
|
+
include Mail::Utilities
|
6
|
+
|
7
|
+
def initialize( string )
|
8
|
+
mime_version = Mail::Parsers::MimeVersionParser.new.parse(string)
|
9
|
+
@major = mime_version.major
|
10
|
+
@minor = mime_version.minor
|
11
|
+
end
|
12
|
+
|
13
|
+
def major
|
14
|
+
@major
|
15
|
+
end
|
16
|
+
|
17
|
+
def minor
|
18
|
+
@minor
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Mail
|
3
|
+
class PhraseList
|
4
|
+
|
5
|
+
include Mail::Utilities
|
6
|
+
|
7
|
+
def initialize(string)
|
8
|
+
@phrase_lists = Mail::Parsers::PhraseListsParser.new.parse(string)
|
9
|
+
end
|
10
|
+
|
11
|
+
def phrases
|
12
|
+
@phrase_lists.phrases.map { |p| unquote(p) }
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Mail
|
3
|
+
class ReceivedElement
|
4
|
+
|
5
|
+
include Mail::Utilities
|
6
|
+
|
7
|
+
def initialize( string )
|
8
|
+
received = Mail::Parsers::ReceivedParser.new.parse(string)
|
9
|
+
@date_time = ::DateTime.parse("#{received.date} #{received.time}")
|
10
|
+
@info = received.info
|
11
|
+
end
|
12
|
+
|
13
|
+
def date_time
|
14
|
+
@date_time
|
15
|
+
end
|
16
|
+
|
17
|
+
def info
|
18
|
+
@info
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s(*args)
|
22
|
+
"#{@info}; #{@date_time.to_s(*args)}"
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,304 @@
|
|
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 =~ /\=\?[^?]+\?[QB]\?[^?]+?\?\=/xmi
|
118
|
+
|
119
|
+
lines = collapse_adjacent_encodings(str)
|
120
|
+
|
121
|
+
# Split on white-space boundaries with capture, so we capture the white-space as well
|
122
|
+
lines.map do |line|
|
123
|
+
line.split(/([ \t])/).map do |text|
|
124
|
+
if text.index('=?').nil?
|
125
|
+
text
|
126
|
+
else
|
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
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end.flatten.join("")
|
145
|
+
end
|
146
|
+
|
147
|
+
# Takes an encoded string of the format =?<encoding>?[QB]?<string>?=
|
148
|
+
def Encodings.unquote_and_convert_to(str, to_encoding)
|
149
|
+
output = value_decode( str ).to_s # output is already converted to UTF-8
|
150
|
+
|
151
|
+
if 'utf8' == to_encoding.to_s.downcase.gsub("-", "")
|
152
|
+
output
|
153
|
+
elsif to_encoding
|
154
|
+
begin
|
155
|
+
if RUBY_VERSION >= '1.9'
|
156
|
+
output.encode(to_encoding)
|
157
|
+
else
|
158
|
+
require 'iconv'
|
159
|
+
Iconv.iconv(to_encoding, 'UTF-8', output).first
|
160
|
+
end
|
161
|
+
rescue Iconv::IllegalSequence, Iconv::InvalidEncoding, Errno::EINVAL
|
162
|
+
# the 'from' parameter specifies a charset other than what the text
|
163
|
+
# actually is...not much we can do in this case but just return the
|
164
|
+
# unconverted text.
|
165
|
+
#
|
166
|
+
# Ditto if either parameter represents an unknown charset, like
|
167
|
+
# X-UNKNOWN.
|
168
|
+
output
|
169
|
+
end
|
170
|
+
else
|
171
|
+
output
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def Encodings.address_encode(address, charset = 'utf-8')
|
176
|
+
if address.is_a?(Array)
|
177
|
+
# loop back through for each element
|
178
|
+
address.compact.map { |a| Encodings.address_encode(a, charset) }.join(", ")
|
179
|
+
else
|
180
|
+
# find any word boundary that is not ascii and encode it
|
181
|
+
encode_non_usascii(address, charset) if address
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def Encodings.encode_non_usascii(address, charset)
|
186
|
+
return address if address.ascii_only? or charset.nil?
|
187
|
+
us_ascii = %Q{\x00-\x7f}
|
188
|
+
# Encode any non usascii strings embedded inside of quotes
|
189
|
+
address = address.gsub(/(".*?[^#{us_ascii}].*?")/) { |s| Encodings.b_value_encode(unquote(s), charset) }
|
190
|
+
# Then loop through all remaining items and encode as needed
|
191
|
+
tokens = address.split(/\s/)
|
192
|
+
map_with_index(tokens) do |word, i|
|
193
|
+
if word.ascii_only?
|
194
|
+
word
|
195
|
+
else
|
196
|
+
previous_non_ascii = i>0 && tokens[i-1] && !tokens[i-1].ascii_only?
|
197
|
+
if previous_non_ascii #why are we adding an extra space here?
|
198
|
+
word = " #{word}"
|
199
|
+
end
|
200
|
+
Encodings.b_value_encode(word, charset)
|
201
|
+
end
|
202
|
+
end.join(' ')
|
203
|
+
end
|
204
|
+
|
205
|
+
# Encode a string with Base64 Encoding and returns it ready to be inserted
|
206
|
+
# as a value for a field, that is, in the =?<charset>?B?<string>?= format
|
207
|
+
#
|
208
|
+
# Example:
|
209
|
+
#
|
210
|
+
# Encodings.b_value_encode('This is あ string', 'UTF-8')
|
211
|
+
# #=> "=?UTF-8?B?VGhpcyBpcyDjgYIgc3RyaW5n?="
|
212
|
+
def Encodings.b_value_encode(encoded_str, encoding = nil)
|
213
|
+
return encoded_str if encoded_str.to_s.ascii_only?
|
214
|
+
string, encoding = RubyVer.b_value_encode(encoded_str, encoding)
|
215
|
+
map_lines(string) do |str|
|
216
|
+
"=?#{encoding}?B?#{str.chomp}?="
|
217
|
+
end.join(" ")
|
218
|
+
end
|
219
|
+
|
220
|
+
# Encode a string with Quoted-Printable Encoding and returns it ready to be inserted
|
221
|
+
# as a value for a field, that is, in the =?<charset>?Q?<string>?= format
|
222
|
+
#
|
223
|
+
# Example:
|
224
|
+
#
|
225
|
+
# Encodings.q_value_encode('This is あ string', 'UTF-8')
|
226
|
+
# #=> "=?UTF-8?Q?This_is_=E3=81=82_string?="
|
227
|
+
def Encodings.q_value_encode(encoded_str, encoding = nil)
|
228
|
+
return encoded_str if encoded_str.to_s.ascii_only?
|
229
|
+
string, encoding = RubyVer.q_value_encode(encoded_str, encoding)
|
230
|
+
string.gsub!("=\r\n", '') # We already have limited the string to the length we want
|
231
|
+
map_lines(string) do |str|
|
232
|
+
"=?#{encoding}?Q?#{str.chomp.gsub(/ /, '_')}?="
|
233
|
+
end.join(" ")
|
234
|
+
end
|
235
|
+
|
236
|
+
private
|
237
|
+
|
238
|
+
# Decodes a Base64 string from the "=?UTF-8?B?VGhpcyBpcyDjgYIgc3RyaW5n?=" format
|
239
|
+
#
|
240
|
+
# Example:
|
241
|
+
#
|
242
|
+
# Encodings.b_value_decode("=?UTF-8?B?VGhpcyBpcyDjgYIgc3RyaW5n?=")
|
243
|
+
# #=> 'This is あ string'
|
244
|
+
def Encodings.b_value_decode(str)
|
245
|
+
RubyVer.b_value_decode(str)
|
246
|
+
end
|
247
|
+
|
248
|
+
# Decodes a Quoted-Printable string from the "=?UTF-8?Q?This_is_=E3=81=82_string?=" format
|
249
|
+
#
|
250
|
+
# Example:
|
251
|
+
#
|
252
|
+
# Encodings.q_value_decode("=?UTF-8?Q?This_is_=E3=81=82_string?=")
|
253
|
+
# #=> 'This is あ string'
|
254
|
+
def Encodings.q_value_decode(str)
|
255
|
+
RubyVer.q_value_decode(str)
|
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
|
269
|
+
end
|
270
|
+
|
271
|
+
# Gets the encoding type (Q or B) from the string.
|
272
|
+
def Encodings.split_value_encoding_from_string(str)
|
273
|
+
match = str.match(/\=\?[^?]+?\?([QB])\?(.+)?\?\=/mi)
|
274
|
+
if match
|
275
|
+
match[1]
|
276
|
+
else
|
277
|
+
nil
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
# When the encoded string consists of multiple lines, lines with the same
|
282
|
+
# encoding (Q or B) can be joined together.
|
283
|
+
#
|
284
|
+
# String has to be of the format =?<encoding>?[QB]?<string>?=
|
285
|
+
def Encodings.collapse_adjacent_encodings(str)
|
286
|
+
lines = str.split(/(\?=)\s*(=\?)/).each_slice(2).map(&:join)
|
287
|
+
results = []
|
288
|
+
previous_encoding = nil
|
289
|
+
|
290
|
+
lines.each do |line|
|
291
|
+
encoding = split_value_encoding_from_string(line)
|
292
|
+
|
293
|
+
if encoding == previous_encoding
|
294
|
+
line = results.pop + line
|
295
|
+
end
|
296
|
+
|
297
|
+
previous_encoding = encoding
|
298
|
+
results << line
|
299
|
+
end
|
300
|
+
|
301
|
+
results
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|