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.
Files changed (137) hide show
  1. data/CHANGELOG.rdoc +555 -0
  2. data/Dependencies.txt +3 -0
  3. data/Gemfile +29 -0
  4. data/README.mkd +583 -0
  5. data/Rakefile +66 -0
  6. data/TODO.rdoc +9 -0
  7. data/lib/VERSION +4 -0
  8. data/lib/mail.rb +89 -0
  9. data/lib/mail/attachments_list.rb +105 -0
  10. data/lib/mail/body.rb +292 -0
  11. data/lib/mail/configuration.rb +73 -0
  12. data/lib/mail/core_extensions/nil.rb +17 -0
  13. data/lib/mail/core_extensions/object.rb +13 -0
  14. data/lib/mail/core_extensions/shellwords.rb +57 -0
  15. data/lib/mail/core_extensions/smtp.rb +25 -0
  16. data/lib/mail/core_extensions/string.rb +31 -0
  17. data/lib/mail/core_extensions/string/access.rb +104 -0
  18. data/lib/mail/core_extensions/string/multibyte.rb +78 -0
  19. data/lib/mail/elements.rb +14 -0
  20. data/lib/mail/elements/address.rb +306 -0
  21. data/lib/mail/elements/address_list.rb +74 -0
  22. data/lib/mail/elements/content_disposition_element.rb +30 -0
  23. data/lib/mail/elements/content_location_element.rb +25 -0
  24. data/lib/mail/elements/content_transfer_encoding_element.rb +24 -0
  25. data/lib/mail/elements/content_type_element.rb +35 -0
  26. data/lib/mail/elements/date_time_element.rb +26 -0
  27. data/lib/mail/elements/envelope_from_element.rb +34 -0
  28. data/lib/mail/elements/message_ids_element.rb +29 -0
  29. data/lib/mail/elements/mime_version_element.rb +26 -0
  30. data/lib/mail/elements/phrase_list.rb +21 -0
  31. data/lib/mail/elements/received_element.rb +30 -0
  32. data/lib/mail/encodings.rb +266 -0
  33. data/lib/mail/encodings/7bit.rb +31 -0
  34. data/lib/mail/encodings/8bit.rb +31 -0
  35. data/lib/mail/encodings/base64.rb +33 -0
  36. data/lib/mail/encodings/binary.rb +31 -0
  37. data/lib/mail/encodings/quoted_printable.rb +38 -0
  38. data/lib/mail/encodings/transfer_encoding.rb +58 -0
  39. data/lib/mail/envelope.rb +35 -0
  40. data/lib/mail/field.rb +224 -0
  41. data/lib/mail/field_list.rb +33 -0
  42. data/lib/mail/fields.rb +35 -0
  43. data/lib/mail/fields/bcc_field.rb +56 -0
  44. data/lib/mail/fields/cc_field.rb +55 -0
  45. data/lib/mail/fields/comments_field.rb +41 -0
  46. data/lib/mail/fields/common/address_container.rb +16 -0
  47. data/lib/mail/fields/common/common_address.rb +125 -0
  48. data/lib/mail/fields/common/common_date.rb +42 -0
  49. data/lib/mail/fields/common/common_field.rb +51 -0
  50. data/lib/mail/fields/common/common_message_id.rb +44 -0
  51. data/lib/mail/fields/common/parameter_hash.rb +58 -0
  52. data/lib/mail/fields/content_description_field.rb +19 -0
  53. data/lib/mail/fields/content_disposition_field.rb +69 -0
  54. data/lib/mail/fields/content_id_field.rb +63 -0
  55. data/lib/mail/fields/content_location_field.rb +42 -0
  56. data/lib/mail/fields/content_transfer_encoding_field.rb +50 -0
  57. data/lib/mail/fields/content_type_field.rb +198 -0
  58. data/lib/mail/fields/date_field.rb +57 -0
  59. data/lib/mail/fields/from_field.rb +55 -0
  60. data/lib/mail/fields/in_reply_to_field.rb +55 -0
  61. data/lib/mail/fields/keywords_field.rb +44 -0
  62. data/lib/mail/fields/message_id_field.rb +83 -0
  63. data/lib/mail/fields/mime_version_field.rb +53 -0
  64. data/lib/mail/fields/optional_field.rb +13 -0
  65. data/lib/mail/fields/received_field.rb +75 -0
  66. data/lib/mail/fields/references_field.rb +55 -0
  67. data/lib/mail/fields/reply_to_field.rb +55 -0
  68. data/lib/mail/fields/resent_bcc_field.rb +55 -0
  69. data/lib/mail/fields/resent_cc_field.rb +55 -0
  70. data/lib/mail/fields/resent_date_field.rb +35 -0
  71. data/lib/mail/fields/resent_from_field.rb +55 -0
  72. data/lib/mail/fields/resent_message_id_field.rb +34 -0
  73. data/lib/mail/fields/resent_sender_field.rb +62 -0
  74. data/lib/mail/fields/resent_to_field.rb +55 -0
  75. data/lib/mail/fields/return_path_field.rb +65 -0
  76. data/lib/mail/fields/sender_field.rb +67 -0
  77. data/lib/mail/fields/structured_field.rb +51 -0
  78. data/lib/mail/fields/subject_field.rb +16 -0
  79. data/lib/mail/fields/to_field.rb +55 -0
  80. data/lib/mail/fields/unstructured_field.rb +182 -0
  81. data/lib/mail/header.rb +265 -0
  82. data/lib/mail/indifferent_hash.rb +146 -0
  83. data/lib/mail/mail.rb +255 -0
  84. data/lib/mail/message.rb +2017 -0
  85. data/lib/mail/multibyte.rb +42 -0
  86. data/lib/mail/multibyte/chars.rb +474 -0
  87. data/lib/mail/multibyte/exceptions.rb +8 -0
  88. data/lib/mail/multibyte/unicode.rb +392 -0
  89. data/lib/mail/multibyte/utils.rb +60 -0
  90. data/lib/mail/network.rb +13 -0
  91. data/lib/mail/network/delivery_methods/file_delivery.rb +40 -0
  92. data/lib/mail/network/delivery_methods/sendmail.rb +62 -0
  93. data/lib/mail/network/delivery_methods/smtp.rb +137 -0
  94. data/lib/mail/network/delivery_methods/smtp_connection.rb +74 -0
  95. data/lib/mail/network/delivery_methods/test_mailer.rb +40 -0
  96. data/lib/mail/network/retriever_methods/base.rb +63 -0
  97. data/lib/mail/network/retriever_methods/imap.rb +158 -0
  98. data/lib/mail/network/retriever_methods/pop3.rb +140 -0
  99. data/lib/mail/network/retriever_methods/test_retriever.rb +47 -0
  100. data/lib/mail/parsers/address_lists.rb +64 -0
  101. data/lib/mail/parsers/address_lists.treetop +19 -0
  102. data/lib/mail/parsers/content_disposition.rb +535 -0
  103. data/lib/mail/parsers/content_disposition.treetop +46 -0
  104. data/lib/mail/parsers/content_location.rb +139 -0
  105. data/lib/mail/parsers/content_location.treetop +20 -0
  106. data/lib/mail/parsers/content_transfer_encoding.rb +162 -0
  107. data/lib/mail/parsers/content_transfer_encoding.treetop +20 -0
  108. data/lib/mail/parsers/content_type.rb +967 -0
  109. data/lib/mail/parsers/content_type.treetop +68 -0
  110. data/lib/mail/parsers/date_time.rb +114 -0
  111. data/lib/mail/parsers/date_time.treetop +11 -0
  112. data/lib/mail/parsers/envelope_from.rb +194 -0
  113. data/lib/mail/parsers/envelope_from.treetop +32 -0
  114. data/lib/mail/parsers/message_ids.rb +45 -0
  115. data/lib/mail/parsers/message_ids.treetop +15 -0
  116. data/lib/mail/parsers/mime_version.rb +144 -0
  117. data/lib/mail/parsers/mime_version.treetop +19 -0
  118. data/lib/mail/parsers/phrase_lists.rb +45 -0
  119. data/lib/mail/parsers/phrase_lists.treetop +15 -0
  120. data/lib/mail/parsers/received.rb +71 -0
  121. data/lib/mail/parsers/received.treetop +11 -0
  122. data/lib/mail/parsers/rfc2045.rb +464 -0
  123. data/lib/mail/parsers/rfc2045.treetop +36 -0
  124. data/lib/mail/parsers/rfc2822.rb +5341 -0
  125. data/lib/mail/parsers/rfc2822.treetop +410 -0
  126. data/lib/mail/parsers/rfc2822_obsolete.rb +3757 -0
  127. data/lib/mail/parsers/rfc2822_obsolete.treetop +241 -0
  128. data/lib/mail/part.rb +116 -0
  129. data/lib/mail/parts_list.rb +51 -0
  130. data/lib/mail/patterns.rb +35 -0
  131. data/lib/mail/utilities.rb +215 -0
  132. data/lib/mail/version.rb +24 -0
  133. data/lib/mail/version_specific/ruby_1_8.rb +98 -0
  134. data/lib/mail/version_specific/ruby_1_9.rb +113 -0
  135. data/lib/tasks/corpus.rake +125 -0
  136. data/lib/tasks/treetop.rake +10 -0
  137. 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,17 @@
1
+ # encoding: utf-8
2
+
3
+ # This is not loaded if ActiveSupport is already loaded
4
+
5
+ class NilClass #:nodoc:
6
+ def blank?
7
+ true
8
+ end
9
+
10
+ def to_crlf
11
+ ''
12
+ end
13
+
14
+ def to_lf
15
+ ''
16
+ end
17
+ end
@@ -0,0 +1,13 @@
1
+ # encoding: utf-8
2
+
3
+ # This is not loaded if ActiveSupport is already loaded
4
+
5
+ class Object
6
+ def blank?
7
+ if respond_to?(:empty?)
8
+ empty?
9
+ else
10
+ !self
11
+ end
12
+ end
13
+ 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&amp;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