mail-trunk 2.3.0
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 +555 -0
- data/Dependencies.txt +3 -0
- data/Gemfile +29 -0
- data/README.mkd +583 -0
- data/Rakefile +66 -0
- data/TODO.rdoc +9 -0
- data/lib/VERSION +4 -0
- data/lib/mail.rb +89 -0
- data/lib/mail/attachments_list.rb +105 -0
- data/lib/mail/body.rb +292 -0
- data/lib/mail/configuration.rb +73 -0
- data/lib/mail/core_extensions/nil.rb +17 -0
- data/lib/mail/core_extensions/object.rb +13 -0
- data/lib/mail/core_extensions/shellwords.rb +57 -0
- data/lib/mail/core_extensions/smtp.rb +25 -0
- data/lib/mail/core_extensions/string.rb +31 -0
- data/lib/mail/core_extensions/string/access.rb +104 -0
- data/lib/mail/core_extensions/string/multibyte.rb +78 -0
- data/lib/mail/elements.rb +14 -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/encodings.rb +266 -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/envelope.rb +35 -0
- data/lib/mail/field.rb +224 -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 +125 -0
- data/lib/mail/fields/common/common_date.rb +42 -0
- data/lib/mail/fields/common/common_field.rb +51 -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 +57 -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 +75 -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 +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 +182 -0
- data/lib/mail/header.rb +265 -0
- data/lib/mail/indifferent_hash.rb +146 -0
- data/lib/mail/mail.rb +255 -0
- data/lib/mail/message.rb +2017 -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 +392 -0
- data/lib/mail/multibyte/utils.rb +60 -0
- data/lib/mail/network.rb +13 -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 +137 -0
- data/lib/mail/network/delivery_methods/smtp_connection.rb +74 -0
- data/lib/mail/network/delivery_methods/test_mailer.rb +40 -0
- data/lib/mail/network/retriever_methods/base.rb +63 -0
- data/lib/mail/network/retriever_methods/imap.rb +158 -0
- data/lib/mail/network/retriever_methods/pop3.rb +140 -0
- data/lib/mail/network/retriever_methods/test_retriever.rb +47 -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 +5341 -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 +51 -0
- data/lib/mail/patterns.rb +35 -0
- data/lib/mail/utilities.rb +215 -0
- data/lib/mail/version.rb +24 -0
- data/lib/mail/version_specific/ruby_1_8.rb +98 -0
- data/lib/mail/version_specific/ruby_1_9.rb +113 -0
- data/lib/tasks/corpus.rake +125 -0
- data/lib/tasks/treetop.rake +10 -0
- metadata +221 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
#
|
|
3
|
+
# Thanks to Nicolas Fouché for this wrapper
|
|
4
|
+
#
|
|
5
|
+
require 'singleton'
|
|
6
|
+
|
|
7
|
+
module Mail
|
|
8
|
+
|
|
9
|
+
# The Configuration class is a Singleton used to hold the default
|
|
10
|
+
# configuration for all Mail objects.
|
|
11
|
+
#
|
|
12
|
+
# Each new mail object gets a copy of these values at initialization
|
|
13
|
+
# which can be overwritten on a per mail object basis.
|
|
14
|
+
class Configuration
|
|
15
|
+
include Singleton
|
|
16
|
+
|
|
17
|
+
def initialize
|
|
18
|
+
@delivery_method = nil
|
|
19
|
+
@retriever_method = nil
|
|
20
|
+
super
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def delivery_method(method = nil, settings = {})
|
|
24
|
+
return @delivery_method if @delivery_method && method.nil?
|
|
25
|
+
@delivery_method = lookup_delivery_method(method).new(settings)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def lookup_delivery_method(method)
|
|
29
|
+
case method
|
|
30
|
+
when nil
|
|
31
|
+
Mail::SMTP
|
|
32
|
+
when :smtp
|
|
33
|
+
Mail::SMTP
|
|
34
|
+
when :sendmail
|
|
35
|
+
Mail::Sendmail
|
|
36
|
+
when :file
|
|
37
|
+
Mail::FileDelivery
|
|
38
|
+
when :smtp_connection
|
|
39
|
+
Mail::SMTPConnection
|
|
40
|
+
when :test
|
|
41
|
+
Mail::TestMailer
|
|
42
|
+
else
|
|
43
|
+
method
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def retriever_method(method = nil, settings = {})
|
|
48
|
+
return @retriever_method if @retriever_method && method.nil?
|
|
49
|
+
@retriever_method = lookup_retriever_method(method).new(settings)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def lookup_retriever_method(method)
|
|
53
|
+
case method
|
|
54
|
+
when nil
|
|
55
|
+
Mail::POP3
|
|
56
|
+
when :pop3
|
|
57
|
+
Mail::POP3
|
|
58
|
+
when :imap
|
|
59
|
+
Mail::IMAP
|
|
60
|
+
when :test
|
|
61
|
+
Mail::TestRetriever
|
|
62
|
+
else
|
|
63
|
+
method
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def param_encode_language(value = nil)
|
|
68
|
+
value ? @encode_language = value : @encode_language ||= 'en'
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
# The following is imported from ruby 1.9.2 shellwords.rb
|
|
4
|
+
#
|
|
5
|
+
module Shellwords
|
|
6
|
+
# Escapes a string so that it can be safely used in a Bourne shell
|
|
7
|
+
# command line.
|
|
8
|
+
#
|
|
9
|
+
# Note that a resulted string should be used unquoted and is not
|
|
10
|
+
# intended for use in double quotes nor in single quotes.
|
|
11
|
+
#
|
|
12
|
+
# open("| grep #{Shellwords.escape(pattern)} file") { |pipe|
|
|
13
|
+
# # ...
|
|
14
|
+
# }
|
|
15
|
+
#
|
|
16
|
+
# +String#shellescape+ is a shorthand for this function.
|
|
17
|
+
#
|
|
18
|
+
# open("| grep #{pattern.shellescape} file") { |pipe|
|
|
19
|
+
# # ...
|
|
20
|
+
# }
|
|
21
|
+
#
|
|
22
|
+
def shellescape(str)
|
|
23
|
+
# An empty argument will be skipped, so return empty quotes.
|
|
24
|
+
return "''" if str.empty?
|
|
25
|
+
|
|
26
|
+
str = str.dup
|
|
27
|
+
|
|
28
|
+
# Process as a single byte sequence because not all shell
|
|
29
|
+
# implementations are multibyte aware.
|
|
30
|
+
str.gsub!(/([^A-Za-z0-9_\-.,:\/@\n])/n, "\\\\\\1")
|
|
31
|
+
|
|
32
|
+
# A LF cannot be escaped with a backslash because a backslash + LF
|
|
33
|
+
# combo is regarded as line continuation and simply ignored.
|
|
34
|
+
str.gsub!(/\n/, "'\n'")
|
|
35
|
+
|
|
36
|
+
return str
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
module_function :shellescape
|
|
40
|
+
|
|
41
|
+
class << self
|
|
42
|
+
alias escape shellescape
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
class String
|
|
48
|
+
# call-seq:
|
|
49
|
+
# str.shellescape => string
|
|
50
|
+
#
|
|
51
|
+
# Escapes +str+ so that it can be safely used in a Bourne shell
|
|
52
|
+
# command line. See +Shellwords::shellescape+ for details.
|
|
53
|
+
#
|
|
54
|
+
def shellescape
|
|
55
|
+
Shellwords.escape(self)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module Net
|
|
3
|
+
class SMTP
|
|
4
|
+
# This is a backport of r30294 from ruby trunk because of a bug in net/smtp.
|
|
5
|
+
# http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=30294
|
|
6
|
+
#
|
|
7
|
+
# Fixed in what will be Ruby 1.9.3 - tlsconnect also does not exist in some early versions of ruby
|
|
8
|
+
remove_method :tlsconnect if defined?(Net::SMTP.new.tlsconnect)
|
|
9
|
+
|
|
10
|
+
def tlsconnect(s)
|
|
11
|
+
verified = false
|
|
12
|
+
s = OpenSSL::SSL::SSLSocket.new s, @ssl_context
|
|
13
|
+
logging "TLS connection started"
|
|
14
|
+
s.sync_close = true
|
|
15
|
+
s.connect
|
|
16
|
+
if @ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE
|
|
17
|
+
s.post_connection_check(@address)
|
|
18
|
+
end
|
|
19
|
+
verified = true
|
|
20
|
+
s
|
|
21
|
+
ensure
|
|
22
|
+
s.close unless verified
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
class String #:nodoc:
|
|
3
|
+
def to_crlf
|
|
4
|
+
gsub(/\n|\r\n|\r/) { "\r\n" }
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def to_lf
|
|
8
|
+
gsub(/\n|\r\n|\r/) { "\n" }
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def blank?
|
|
12
|
+
self !~ /\S/
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
unless method_defined?(:ascii_only?)
|
|
16
|
+
# Provides all strings with the Ruby 1.9 method of .ascii_only? and
|
|
17
|
+
# returns true or false
|
|
18
|
+
US_ASCII_REGEXP = %Q{\x00-\x7f}
|
|
19
|
+
def ascii_only?
|
|
20
|
+
!(self =~ /[^#{US_ASCII_REGEXP}]/)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def not_ascii_only?
|
|
25
|
+
!ascii_only?
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
unless method_defined?(:bytesize)
|
|
29
|
+
alias :bytesize :length
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
# This is not loaded if ActiveSupport is already loaded
|
|
4
|
+
|
|
5
|
+
# This is an almost cut and paste from ActiveSupport v3.0.6, copied in here so that Mail
|
|
6
|
+
# itself does not depend on ActiveSupport to avoid versioning conflicts
|
|
7
|
+
|
|
8
|
+
class String
|
|
9
|
+
unless '1.9'.respond_to?(:force_encoding)
|
|
10
|
+
# Returns the character at the +position+ treating the string as an array (where 0 is the first character).
|
|
11
|
+
#
|
|
12
|
+
# Examples:
|
|
13
|
+
# "hello".at(0) # => "h"
|
|
14
|
+
# "hello".at(4) # => "o"
|
|
15
|
+
# "hello".at(10) # => ERROR if < 1.9, nil in 1.9
|
|
16
|
+
def at(position)
|
|
17
|
+
mb_chars[position, 1].to_s
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Returns the remaining of the string from the +position+ treating the string as an array (where 0 is the first character).
|
|
21
|
+
#
|
|
22
|
+
# Examples:
|
|
23
|
+
# "hello".from(0) # => "hello"
|
|
24
|
+
# "hello".from(2) # => "llo"
|
|
25
|
+
# "hello".from(10) # => "" if < 1.9, nil in 1.9
|
|
26
|
+
def from(position)
|
|
27
|
+
mb_chars[position..-1].to_s
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Returns the beginning of the string up to the +position+ treating the string as an array (where 0 is the first character).
|
|
31
|
+
#
|
|
32
|
+
# Examples:
|
|
33
|
+
# "hello".to(0) # => "h"
|
|
34
|
+
# "hello".to(2) # => "hel"
|
|
35
|
+
# "hello".to(10) # => "hello"
|
|
36
|
+
def to(position)
|
|
37
|
+
mb_chars[0..position].to_s
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Returns the first character of the string or the first +limit+ characters.
|
|
41
|
+
#
|
|
42
|
+
# Examples:
|
|
43
|
+
# "hello".first # => "h"
|
|
44
|
+
# "hello".first(2) # => "he"
|
|
45
|
+
# "hello".first(10) # => "hello"
|
|
46
|
+
def first(limit = 1)
|
|
47
|
+
if limit == 0
|
|
48
|
+
''
|
|
49
|
+
elsif limit >= size
|
|
50
|
+
self
|
|
51
|
+
else
|
|
52
|
+
mb_chars[0...limit].to_s
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Returns the last character of the string or the last +limit+ characters.
|
|
57
|
+
#
|
|
58
|
+
# Examples:
|
|
59
|
+
# "hello".last # => "o"
|
|
60
|
+
# "hello".last(2) # => "lo"
|
|
61
|
+
# "hello".last(10) # => "hello"
|
|
62
|
+
def last(limit = 1)
|
|
63
|
+
if limit == 0
|
|
64
|
+
''
|
|
65
|
+
elsif limit >= size
|
|
66
|
+
self
|
|
67
|
+
else
|
|
68
|
+
mb_chars[(-limit)..-1].to_s
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
else
|
|
72
|
+
def at(position)
|
|
73
|
+
self[position]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def from(position)
|
|
77
|
+
self[position..-1]
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def to(position)
|
|
81
|
+
self[0..position]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def first(limit = 1)
|
|
85
|
+
if limit == 0
|
|
86
|
+
''
|
|
87
|
+
elsif limit >= size
|
|
88
|
+
self
|
|
89
|
+
else
|
|
90
|
+
to(limit - 1)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def last(limit = 1)
|
|
95
|
+
if limit == 0
|
|
96
|
+
''
|
|
97
|
+
elsif limit >= size
|
|
98
|
+
self
|
|
99
|
+
else
|
|
100
|
+
from(-limit)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
# This is not loaded if ActiveSupport is already loaded
|
|
4
|
+
|
|
5
|
+
# This is an almost cut and paste from ActiveSupport v3.0.6, copied in here so that Mail
|
|
6
|
+
# itself does not depend on ActiveSupport to avoid versioning conflicts
|
|
7
|
+
|
|
8
|
+
require 'mail/multibyte'
|
|
9
|
+
|
|
10
|
+
class String
|
|
11
|
+
if RUBY_VERSION >= "1.9"
|
|
12
|
+
# == Multibyte proxy
|
|
13
|
+
#
|
|
14
|
+
# +mb_chars+ is a multibyte safe proxy for string methods.
|
|
15
|
+
#
|
|
16
|
+
# In Ruby 1.8 and older it creates and returns an instance of the Mail::Multibyte::Chars class which
|
|
17
|
+
# encapsulates the original string. A Unicode safe version of all the String methods are defined on this proxy
|
|
18
|
+
# class. If the proxy class doesn't respond to a certain method, it's forwarded to the encapsuled string.
|
|
19
|
+
#
|
|
20
|
+
# name = 'Claus Müller'
|
|
21
|
+
# name.reverse # => "rell??M sualC"
|
|
22
|
+
# name.length # => 13
|
|
23
|
+
#
|
|
24
|
+
# name.mb_chars.reverse.to_s # => "rellüM sualC"
|
|
25
|
+
# name.mb_chars.length # => 12
|
|
26
|
+
#
|
|
27
|
+
# In Ruby 1.9 and newer +mb_chars+ returns +self+ because String is (mostly) encoding aware. This means that
|
|
28
|
+
# it becomes easy to run one version of your code on multiple Ruby versions.
|
|
29
|
+
#
|
|
30
|
+
# == Method chaining
|
|
31
|
+
#
|
|
32
|
+
# All the methods on the Chars proxy which normally return a string will return a Chars object. This allows
|
|
33
|
+
# method chaining on the result of any of these methods.
|
|
34
|
+
#
|
|
35
|
+
# name.mb_chars.reverse.length # => 12
|
|
36
|
+
#
|
|
37
|
+
# == Interoperability and configuration
|
|
38
|
+
#
|
|
39
|
+
# The Chars object tries to be as interchangeable with String objects as possible: sorting and comparing between
|
|
40
|
+
# String and Char work like expected. The bang! methods change the internal string representation in the Chars
|
|
41
|
+
# object. Interoperability problems can be resolved easily with a +to_s+ call.
|
|
42
|
+
#
|
|
43
|
+
# For more information about the methods defined on the Chars proxy see Mail::Multibyte::Chars. For
|
|
44
|
+
# information about how to change the default Multibyte behaviour see Mail::Multibyte.
|
|
45
|
+
def mb_chars
|
|
46
|
+
if Mail::Multibyte.proxy_class.consumes?(self)
|
|
47
|
+
Mail::Multibyte.proxy_class.new(self)
|
|
48
|
+
else
|
|
49
|
+
self
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def is_utf8? #:nodoc
|
|
54
|
+
case encoding
|
|
55
|
+
when Encoding::UTF_8
|
|
56
|
+
valid_encoding?
|
|
57
|
+
when Encoding::ASCII_8BIT, Encoding::US_ASCII
|
|
58
|
+
dup.force_encoding(Encoding::UTF_8).valid_encoding?
|
|
59
|
+
else
|
|
60
|
+
false
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
else
|
|
64
|
+
def mb_chars
|
|
65
|
+
if Mail::Multibyte.proxy_class.wants?(self)
|
|
66
|
+
Mail::Multibyte.proxy_class.new(self)
|
|
67
|
+
else
|
|
68
|
+
self
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Returns true if the string has UTF-8 semantics (a String used for purely byte resources is unlikely to have
|
|
73
|
+
# them), returns false otherwise.
|
|
74
|
+
def is_utf8?
|
|
75
|
+
Mail::Multibyte::Chars.consumes?(self)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module Mail
|
|
2
|
+
autoload :Address, 'mail/elements/address'
|
|
3
|
+
autoload :AddressList, 'mail/elements/address_list'
|
|
4
|
+
autoload :ContentDispositionElement, 'mail/elements/content_disposition_element'
|
|
5
|
+
autoload :ContentLocationElement, 'mail/elements/content_location_element'
|
|
6
|
+
autoload :ContentTransferEncodingElement, 'mail/elements/content_transfer_encoding_element'
|
|
7
|
+
autoload :ContentTypeElement, 'mail/elements/content_type_element'
|
|
8
|
+
autoload :DateTimeElement, 'mail/elements/date_time_element'
|
|
9
|
+
autoload :EnvelopeFromElement, 'mail/elements/envelope_from_element'
|
|
10
|
+
autoload :MessageIdsElement, 'mail/elements/message_ids_element'
|
|
11
|
+
autoload :MimeVersionElement, 'mail/elements/mime_version_element'
|
|
12
|
+
autoload :PhraseList, 'mail/elements/phrase_list'
|
|
13
|
+
autoload :ReceivedElement, 'mail/elements/received_element'
|
|
14
|
+
end
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module Mail
|
|
3
|
+
class Address
|
|
4
|
+
|
|
5
|
+
include Mail::Utilities
|
|
6
|
+
|
|
7
|
+
# Mail::Address handles all email addresses in Mail. It takes an email address string
|
|
8
|
+
# and parses it, breaking it down into it's component parts and allowing you to get the
|
|
9
|
+
# address, comments, display name, name, local part, domain part and fully formatted
|
|
10
|
+
# address.
|
|
11
|
+
#
|
|
12
|
+
# Mail::Address requires a correctly formatted email address per RFC2822 or RFC822. It
|
|
13
|
+
# handles all obsolete versions including obsolete domain routing on the local part.
|
|
14
|
+
#
|
|
15
|
+
# a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
|
|
16
|
+
# a.format #=> 'Mikel Lindsaar <mikel@test.lindsaar.net> (My email address)'
|
|
17
|
+
# a.address #=> 'mikel@test.lindsaar.net'
|
|
18
|
+
# a.display_name #=> 'Mikel Lindsaar'
|
|
19
|
+
# a.local #=> 'mikel'
|
|
20
|
+
# a.domain #=> 'test.lindsaar.net'
|
|
21
|
+
# a.comments #=> ['My email address']
|
|
22
|
+
# a.to_s #=> 'Mikel Lindsaar <mikel@test.lindsaar.net> (My email address)'
|
|
23
|
+
def initialize(value = nil)
|
|
24
|
+
@output_type = nil
|
|
25
|
+
@tree = nil
|
|
26
|
+
@raw_text = value
|
|
27
|
+
case
|
|
28
|
+
when value.nil?
|
|
29
|
+
@parsed = false
|
|
30
|
+
return
|
|
31
|
+
else
|
|
32
|
+
parse(value)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Returns the raw imput of the passed in string, this is before it is passed
|
|
37
|
+
# by the parser.
|
|
38
|
+
def raw
|
|
39
|
+
@raw_text
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Returns a correctly formatted address for the email going out. If given
|
|
43
|
+
# an incorrectly formatted address as input, Mail::Address will do it's best
|
|
44
|
+
# to format it correctly. This includes quoting display names as needed and
|
|
45
|
+
# putting the address in angle brackets etc.
|
|
46
|
+
#
|
|
47
|
+
# a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
|
|
48
|
+
# a.format #=> 'Mikel Lindsaar <mikel@test.lindsaar.net> (My email address)'
|
|
49
|
+
def format
|
|
50
|
+
parse unless @parsed
|
|
51
|
+
case
|
|
52
|
+
when tree.nil?
|
|
53
|
+
''
|
|
54
|
+
when display_name
|
|
55
|
+
[quote_phrase(display_name), "<#{address}>", format_comments].compact.join(" ")
|
|
56
|
+
else
|
|
57
|
+
[address, format_comments].compact.join(" ")
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Returns the address that is in the address itself. That is, the
|
|
62
|
+
# local@domain string, without any angle brackets or the like.
|
|
63
|
+
#
|
|
64
|
+
# a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
|
|
65
|
+
# a.address #=> 'mikel@test.lindsaar.net'
|
|
66
|
+
def address
|
|
67
|
+
parse unless @parsed
|
|
68
|
+
domain ? "#{local}@#{domain}" : local
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Provides a way to assign an address to an already made Mail::Address object.
|
|
72
|
+
#
|
|
73
|
+
# a = Address.new
|
|
74
|
+
# a.address = 'Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>'
|
|
75
|
+
# a.address #=> 'mikel@test.lindsaar.net'
|
|
76
|
+
def address=(value)
|
|
77
|
+
parse(value)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Returns the display name of the email address passed in.
|
|
81
|
+
#
|
|
82
|
+
# a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
|
|
83
|
+
# a.display_name #=> 'Mikel Lindsaar'
|
|
84
|
+
def display_name
|
|
85
|
+
parse unless @parsed
|
|
86
|
+
@display_name ||= get_display_name
|
|
87
|
+
Encodings.decode_encode(@display_name.to_s, @output_type) if @display_name
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Provides a way to assign a display name to an already made Mail::Address object.
|
|
91
|
+
#
|
|
92
|
+
# a = Address.new
|
|
93
|
+
# a.address = 'mikel@test.lindsaar.net'
|
|
94
|
+
# a.display_name = 'Mikel Lindsaar'
|
|
95
|
+
# a.format #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>'
|
|
96
|
+
def display_name=( str )
|
|
97
|
+
@display_name = str
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Returns the local part (the left hand side of the @ sign in the email address) of
|
|
101
|
+
# the address
|
|
102
|
+
#
|
|
103
|
+
# a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
|
|
104
|
+
# a.local #=> 'mikel'
|
|
105
|
+
def local
|
|
106
|
+
parse unless @parsed
|
|
107
|
+
"#{obs_domain_list}#{get_local.strip}" if get_local
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Returns the domain part (the right hand side of the @ sign in the email address) of
|
|
111
|
+
# the address
|
|
112
|
+
#
|
|
113
|
+
# a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
|
|
114
|
+
# a.domain #=> 'test.lindsaar.net'
|
|
115
|
+
def domain
|
|
116
|
+
parse unless @parsed
|
|
117
|
+
strip_all_comments(get_domain) if get_domain
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Returns an array of comments that are in the email, or an empty array if there
|
|
121
|
+
# are no comments
|
|
122
|
+
#
|
|
123
|
+
# a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
|
|
124
|
+
# a.comments #=> ['My email address']
|
|
125
|
+
def comments
|
|
126
|
+
parse unless @parsed
|
|
127
|
+
if get_comments.empty?
|
|
128
|
+
nil
|
|
129
|
+
else
|
|
130
|
+
get_comments.map { |c| c.squeeze(" ") }
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Sometimes an address will not have a display name, but might have the name
|
|
135
|
+
# as a comment field after the address. This returns that name if it exists.
|
|
136
|
+
#
|
|
137
|
+
# a = Address.new('mikel@test.lindsaar.net (Mikel Lindsaar)')
|
|
138
|
+
# a.name #=> 'Mikel Lindsaar'
|
|
139
|
+
def name
|
|
140
|
+
parse unless @parsed
|
|
141
|
+
get_name
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Returns the format of the address, or returns nothing
|
|
145
|
+
#
|
|
146
|
+
# a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
|
|
147
|
+
# a.format #=> 'Mikel Lindsaar <mikel@test.lindsaar.net> (My email address)'
|
|
148
|
+
def to_s
|
|
149
|
+
parse unless @parsed
|
|
150
|
+
format
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Shows the Address object basic details, including the Address
|
|
154
|
+
# a = Address.new('Mikel (My email) <mikel@test.lindsaar.net>')
|
|
155
|
+
# a.inspect #=> "#<Mail::Address:14184910 Address: |Mikel <mikel@test.lindsaar.net> (My email)| >"
|
|
156
|
+
def inspect
|
|
157
|
+
parse unless @parsed
|
|
158
|
+
"#<#{self.class}:#{self.object_id} Address: |#{to_s}| >"
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def encoded
|
|
162
|
+
@output_type = :encode
|
|
163
|
+
format
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def decoded
|
|
167
|
+
@output_type = :decode
|
|
168
|
+
format
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
private
|
|
172
|
+
|
|
173
|
+
def parse(value = nil)
|
|
174
|
+
@parsed = true
|
|
175
|
+
case
|
|
176
|
+
when value.nil?
|
|
177
|
+
nil
|
|
178
|
+
when value.class == String
|
|
179
|
+
self.tree = Mail::AddressList.new(value).address_nodes.first
|
|
180
|
+
else
|
|
181
|
+
self.tree = value
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def get_domain
|
|
187
|
+
if tree.respond_to?(:angle_addr) && tree.angle_addr.respond_to?(:addr_spec) && tree.angle_addr.addr_spec.respond_to?(:domain)
|
|
188
|
+
@domain_text ||= tree.angle_addr.addr_spec.domain.text_value.strip
|
|
189
|
+
elsif tree.respond_to?(:domain)
|
|
190
|
+
@domain_text ||= tree.domain.text_value.strip
|
|
191
|
+
elsif tree.respond_to?(:addr_spec) && tree.addr_spec.respond_to?(:domain)
|
|
192
|
+
tree.addr_spec.domain.text_value.strip
|
|
193
|
+
else
|
|
194
|
+
nil
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def strip_all_comments(string)
|
|
199
|
+
unless comments.blank?
|
|
200
|
+
comments.each do |comment|
|
|
201
|
+
string = string.gsub("(#{comment})", '')
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
string.strip
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def strip_domain_comments(value)
|
|
208
|
+
unless comments.blank?
|
|
209
|
+
comments.each do |comment|
|
|
210
|
+
if get_domain && get_domain.include?("(#{comment})")
|
|
211
|
+
value = value.gsub("(#{comment})", '')
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
value.to_s.strip
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def get_comments
|
|
219
|
+
if tree.respond_to?(:comments)
|
|
220
|
+
@comments = tree.comments.map { |c| unparen(c.text_value.to_str) }
|
|
221
|
+
else
|
|
222
|
+
@comments = []
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def get_display_name
|
|
227
|
+
if tree.respond_to?(:display_name)
|
|
228
|
+
name = unquote(tree.display_name.text_value.strip)
|
|
229
|
+
str = strip_all_comments(name.to_s)
|
|
230
|
+
elsif comments
|
|
231
|
+
if domain
|
|
232
|
+
str = strip_domain_comments(format_comments)
|
|
233
|
+
else
|
|
234
|
+
str = nil
|
|
235
|
+
end
|
|
236
|
+
else
|
|
237
|
+
nil
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
if str.blank?
|
|
241
|
+
nil
|
|
242
|
+
else
|
|
243
|
+
str
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def get_name
|
|
248
|
+
if display_name
|
|
249
|
+
str = display_name
|
|
250
|
+
else
|
|
251
|
+
if comments
|
|
252
|
+
comment_text = comments.join(' ').squeeze(" ")
|
|
253
|
+
str = "(#{comment_text})"
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
if str.blank?
|
|
258
|
+
nil
|
|
259
|
+
else
|
|
260
|
+
unparen(str)
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
# Provides access to the Treetop parse tree for this address
|
|
265
|
+
def tree
|
|
266
|
+
@tree
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
def tree=(value)
|
|
270
|
+
@tree = value
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def format_comments
|
|
274
|
+
if comments
|
|
275
|
+
comment_text = comments.map {|c| escape_paren(c) }.join(' ').squeeze(" ")
|
|
276
|
+
@format_comments ||= "(#{comment_text})"
|
|
277
|
+
else
|
|
278
|
+
nil
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
def obs_domain_list
|
|
283
|
+
if tree.respond_to?(:angle_addr)
|
|
284
|
+
obs = tree.angle_addr.elements.select { |e| e.respond_to?(:obs_domain_list) }
|
|
285
|
+
!obs.empty? ? obs.first.text_value : nil
|
|
286
|
+
else
|
|
287
|
+
nil
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
def get_local
|
|
292
|
+
case
|
|
293
|
+
when tree.respond_to?(:local_dot_atom_text)
|
|
294
|
+
tree.local_dot_atom_text.text_value
|
|
295
|
+
when tree.respond_to?(:angle_addr) && tree.angle_addr.respond_to?(:addr_spec) && tree.angle_addr.addr_spec.respond_to?(:local_part)
|
|
296
|
+
tree.angle_addr.addr_spec.local_part.text_value
|
|
297
|
+
when tree.respond_to?(:addr_spec) && tree.addr_spec.respond_to?(:local_part)
|
|
298
|
+
tree.addr_spec.local_part.text_value
|
|
299
|
+
else
|
|
300
|
+
tree && tree.respond_to?(:local_part) ? tree.local_part.text_value : nil
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
end
|
|
306
|
+
end
|