mail 2.4.4 → 2.6.3

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 (163) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.rdoc +156 -0
  3. data/CONTRIBUTING.md +21 -6
  4. data/Dependencies.txt +0 -1
  5. data/Gemfile +10 -21
  6. data/MIT-LICENSE +20 -0
  7. data/README.md +52 -32
  8. data/Rakefile +11 -22
  9. data/lib/mail/attachments_list.rb +2 -2
  10. data/lib/mail/body.rb +33 -9
  11. data/lib/mail/check_delivery_params.rb +20 -0
  12. data/lib/mail/configuration.rb +1 -1
  13. data/lib/mail/{patterns.rb → constants.rb} +26 -6
  14. data/lib/mail/core_extensions/nil.rb +4 -2
  15. data/lib/mail/core_extensions/object.rb +8 -8
  16. data/lib/mail/core_extensions/smtp.rb +12 -13
  17. data/lib/mail/core_extensions/string.rb +19 -6
  18. data/lib/mail/elements/address.rb +82 -141
  19. data/lib/mail/elements/address_list.rb +9 -42
  20. data/lib/mail/elements/content_disposition_element.rb +3 -7
  21. data/lib/mail/elements/content_location_element.rb +2 -6
  22. data/lib/mail/elements/content_transfer_encoding_element.rb +3 -10
  23. data/lib/mail/elements/content_type_element.rb +4 -8
  24. data/lib/mail/elements/date_time_element.rb +3 -7
  25. data/lib/mail/elements/envelope_from_element.rb +18 -13
  26. data/lib/mail/elements/message_ids_element.rb +1 -6
  27. data/lib/mail/elements/mime_version_element.rb +3 -7
  28. data/lib/mail/elements/phrase_list.rb +2 -7
  29. data/lib/mail/elements/received_element.rb +3 -7
  30. data/lib/mail/elements.rb +12 -12
  31. data/lib/mail/encodings/quoted_printable.rb +4 -3
  32. data/lib/mail/encodings/unix_to_unix.rb +17 -0
  33. data/lib/mail/encodings.rb +53 -38
  34. data/lib/mail/envelope.rb +0 -5
  35. data/lib/mail/field.rb +131 -118
  36. data/lib/mail/field_list.rb +18 -18
  37. data/lib/mail/fields/bcc_field.rb +2 -2
  38. data/lib/mail/fields/cc_field.rb +2 -2
  39. data/lib/mail/fields/comments_field.rb +1 -1
  40. data/lib/mail/fields/common/common_address.rb +32 -22
  41. data/lib/mail/fields/common/common_date.rb +0 -7
  42. data/lib/mail/fields/common/common_field.rb +10 -3
  43. data/lib/mail/fields/common/common_message_id.rb +9 -5
  44. data/lib/mail/fields/common/parameter_hash.rb +3 -3
  45. data/lib/mail/fields/content_disposition_field.rb +1 -0
  46. data/lib/mail/fields/content_id_field.rb +1 -2
  47. data/lib/mail/fields/content_transfer_encoding_field.rb +2 -8
  48. data/lib/mail/fields/content_type_field.rb +5 -2
  49. data/lib/mail/fields/date_field.rb +14 -14
  50. data/lib/mail/fields/from_field.rb +2 -2
  51. data/lib/mail/fields/in_reply_to_field.rb +2 -1
  52. data/lib/mail/fields/keywords_field.rb +1 -1
  53. data/lib/mail/fields/message_id_field.rb +2 -3
  54. data/lib/mail/fields/references_field.rb +2 -1
  55. data/lib/mail/fields/reply_to_field.rb +2 -2
  56. data/lib/mail/fields/resent_bcc_field.rb +2 -2
  57. data/lib/mail/fields/resent_cc_field.rb +2 -2
  58. data/lib/mail/fields/resent_from_field.rb +2 -2
  59. data/lib/mail/fields/resent_sender_field.rb +3 -3
  60. data/lib/mail/fields/resent_to_field.rb +2 -2
  61. data/lib/mail/fields/sender_field.rb +8 -8
  62. data/lib/mail/fields/to_field.rb +2 -2
  63. data/lib/mail/fields/unstructured_field.rb +44 -31
  64. data/lib/mail/fields.rb +32 -32
  65. data/lib/mail/header.rb +47 -37
  66. data/lib/mail/mail.rb +15 -1
  67. data/lib/mail/matchers/has_sent_mail.rb +36 -3
  68. data/lib/mail/message.rb +164 -57
  69. data/lib/mail/multibyte/chars.rb +4 -4
  70. data/lib/mail/multibyte/unicode.rb +9 -1
  71. data/lib/mail/network/delivery_methods/exim.rb +2 -3
  72. data/lib/mail/network/delivery_methods/file_delivery.rb +5 -0
  73. data/lib/mail/network/delivery_methods/sendmail.rb +35 -8
  74. data/lib/mail/network/delivery_methods/smtp.rb +34 -45
  75. data/lib/mail/network/delivery_methods/smtp_connection.rb +9 -22
  76. data/lib/mail/network/delivery_methods/test_mailer.rb +6 -2
  77. data/lib/mail/network/retriever_methods/imap.rb +32 -19
  78. data/lib/mail/network/retriever_methods/pop3.rb +2 -2
  79. data/lib/mail/network/retriever_methods/test_retriever.rb +11 -15
  80. data/lib/mail/network.rb +9 -9
  81. data/lib/mail/parsers/address_lists_parser.rb +132 -0
  82. data/lib/mail/parsers/content_disposition_parser.rb +67 -0
  83. data/lib/mail/parsers/content_location_parser.rb +35 -0
  84. data/lib/mail/parsers/content_transfer_encoding_parser.rb +33 -0
  85. data/lib/mail/parsers/content_type_parser.rb +66 -0
  86. data/lib/mail/parsers/date_time_parser.rb +36 -0
  87. data/lib/mail/parsers/envelope_from_parser.rb +45 -0
  88. data/lib/mail/parsers/message_ids_parser.rb +39 -0
  89. data/lib/mail/parsers/mime_version_parser.rb +41 -0
  90. data/lib/mail/parsers/phrase_lists_parser.rb +33 -0
  91. data/lib/mail/parsers/ragel/common.rl +185 -0
  92. data/lib/mail/parsers/ragel/date_time.rl +30 -0
  93. data/lib/mail/parsers/ragel/parser_info.rb +61 -0
  94. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb +14864 -0
  95. data/lib/mail/parsers/ragel/ruby/machines/address_lists_machine.rb.rl +37 -0
  96. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb +751 -0
  97. data/lib/mail/parsers/ragel/ruby/machines/content_disposition_machine.rb.rl +37 -0
  98. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb +614 -0
  99. data/lib/mail/parsers/ragel/ruby/machines/content_location_machine.rb.rl +37 -0
  100. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb +447 -0
  101. data/lib/mail/parsers/ragel/ruby/machines/content_transfer_encoding_machine.rb.rl +37 -0
  102. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb +825 -0
  103. data/lib/mail/parsers/ragel/ruby/machines/content_type_machine.rb.rl +37 -0
  104. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb +817 -0
  105. data/lib/mail/parsers/ragel/ruby/machines/date_time_machine.rb.rl +37 -0
  106. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb +2149 -0
  107. data/lib/mail/parsers/ragel/ruby/machines/envelope_from_machine.rb.rl +37 -0
  108. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb +1570 -0
  109. data/lib/mail/parsers/ragel/ruby/machines/message_ids_machine.rb.rl +37 -0
  110. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb +440 -0
  111. data/lib/mail/parsers/ragel/ruby/machines/mime_version_machine.rb.rl +37 -0
  112. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb +564 -0
  113. data/lib/mail/parsers/ragel/ruby/machines/phrase_lists_machine.rb.rl +37 -0
  114. data/lib/mail/parsers/ragel/ruby/machines/rb_actions.rl +51 -0
  115. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb +5144 -0
  116. data/lib/mail/parsers/ragel/ruby/machines/received_machine.rb.rl +37 -0
  117. data/lib/mail/parsers/ragel/ruby/parser.rb.rl.erb +37 -0
  118. data/lib/mail/parsers/ragel/ruby.rb +40 -0
  119. data/lib/mail/parsers/ragel.rb +17 -0
  120. data/lib/mail/parsers/received_parser.rb +47 -0
  121. data/lib/mail/parsers.rb +26 -0
  122. data/lib/mail/part.rb +6 -2
  123. data/lib/mail/parts_list.rb +5 -3
  124. data/lib/mail/utilities.rb +70 -60
  125. data/lib/mail/values/unicode_tables.dat +0 -0
  126. data/lib/mail/version.rb +7 -15
  127. data/lib/mail/version_specific/ruby_1_8.rb +25 -4
  128. data/lib/mail/version_specific/ruby_1_9.rb +110 -30
  129. data/lib/mail.rb +20 -19
  130. metadata +129 -73
  131. data/Gemfile.lock +0 -36
  132. data/lib/VERSION +0 -4
  133. data/lib/mail/core_extensions/shell_escape.rb +0 -56
  134. data/lib/mail/parsers/address_lists.rb +0 -64
  135. data/lib/mail/parsers/address_lists.treetop +0 -19
  136. data/lib/mail/parsers/content_disposition.rb +0 -535
  137. data/lib/mail/parsers/content_disposition.treetop +0 -46
  138. data/lib/mail/parsers/content_location.rb +0 -139
  139. data/lib/mail/parsers/content_location.treetop +0 -20
  140. data/lib/mail/parsers/content_transfer_encoding.rb +0 -162
  141. data/lib/mail/parsers/content_transfer_encoding.treetop +0 -20
  142. data/lib/mail/parsers/content_type.rb +0 -967
  143. data/lib/mail/parsers/content_type.treetop +0 -68
  144. data/lib/mail/parsers/date_time.rb +0 -114
  145. data/lib/mail/parsers/date_time.treetop +0 -11
  146. data/lib/mail/parsers/envelope_from.rb +0 -194
  147. data/lib/mail/parsers/envelope_from.treetop +0 -32
  148. data/lib/mail/parsers/message_ids.rb +0 -45
  149. data/lib/mail/parsers/message_ids.treetop +0 -15
  150. data/lib/mail/parsers/mime_version.rb +0 -144
  151. data/lib/mail/parsers/mime_version.treetop +0 -19
  152. data/lib/mail/parsers/phrase_lists.rb +0 -45
  153. data/lib/mail/parsers/phrase_lists.treetop +0 -15
  154. data/lib/mail/parsers/received.rb +0 -71
  155. data/lib/mail/parsers/received.treetop +0 -11
  156. data/lib/mail/parsers/rfc2045.rb +0 -464
  157. data/lib/mail/parsers/rfc2045.treetop +0 -36
  158. data/lib/mail/parsers/rfc2822.rb +0 -5341
  159. data/lib/mail/parsers/rfc2822.treetop +0 -410
  160. data/lib/mail/parsers/rfc2822_obsolete.rb +0 -3768
  161. data/lib/mail/parsers/rfc2822_obsolete.treetop +0 -241
  162. data/lib/tasks/corpus.rake +0 -125
  163. data/lib/tasks/treetop.rake +0 -10
@@ -1,29 +1,31 @@
1
1
  # encoding: us-ascii
2
2
  module Mail
3
- module Patterns
3
+ module Constants
4
4
  white_space = %Q|\x9\x20|
5
5
  text = %Q|\x1-\x8\xB\xC\xE-\x7f|
6
6
  field_name = %Q|\x21-\x39\x3b-\x7e|
7
7
  qp_safe = %Q|\x20-\x3c\x3e-\x7e|
8
-
8
+
9
9
  aspecial = %Q|()<>[]:;@\\,."| # RFC5322
10
10
  tspecial = %Q|()<>@,;:\\"/[]?=| # RFC2045
11
- lwsp = %Q| \t\r\n|
12
11
  sp = %Q| |
13
12
  control = %Q|\x00-\x1f\x7f-\xff|
14
-
13
+
15
14
  if control.respond_to?(:force_encoding)
16
15
  control = control.force_encoding(Encoding::BINARY)
17
16
  end
18
-
17
+
19
18
  CRLF = /\r\n/
20
19
  WSP = /[#{white_space}]/
21
20
  FWS = /#{CRLF}#{WSP}*/
22
21
  TEXT = /[#{text}]/ # + obs-text
23
22
  FIELD_NAME = /[#{field_name}]+/
24
- FIELD_BODY = /.+/
23
+ FIELD_PREFIX = /\A(#{FIELD_NAME})/
24
+ FIELD_BODY = /.+/m
25
25
  FIELD_LINE = /^[#{field_name}]+:\s*.+$/
26
+ FIELD_SPLIT = /^(#{FIELD_NAME})\s*:\s*(#{FIELD_BODY})?$/
26
27
  HEADER_LINE = /^([#{field_name}]+:\s*.+)$/
28
+ HEADER_SPLIT = /#{CRLF}(?!#{WSP})/
27
29
 
28
30
  QP_UNSAFE = /[^#{qp_safe}]/
29
31
  QP_SAFE = /[#{qp_safe}]/
@@ -31,5 +33,23 @@ module Mail
31
33
  ATOM_UNSAFE = /[#{Regexp.quote aspecial}#{control}#{sp}]/n
32
34
  PHRASE_UNSAFE = /[#{Regexp.quote aspecial}#{control}]/n
33
35
  TOKEN_UNSAFE = /[#{Regexp.quote tspecial}#{control}#{sp}]/n
36
+ ENCODED_VALUE = /\=\?[^?]+\?([QB])\?[^?]*?\?\=/mi
37
+
38
+ EMPTY = ''
39
+ SPACE = ' '
40
+ UNDERSCORE = '_'
41
+ HYPHEN = '-'
42
+ COLON = ':'
43
+ ASTERISK = '*'
44
+ CR = "\r"
45
+ LF = "\n"
46
+ CR_ENCODED = "=0D"
47
+ LF_ENCODED = "=0A"
48
+ CAPITAL_M = 'M'
49
+ EQUAL_LF = "=\n"
50
+ NULL_SENDER = '<>'
51
+
52
+ Q_VALUES = ['Q','q']
53
+ B_VALUES = ['B','b']
34
54
  end
35
55
  end
@@ -3,8 +3,10 @@
3
3
  # This is not loaded if ActiveSupport is already loaded
4
4
 
5
5
  class NilClass #:nodoc:
6
- def blank?
7
- true
6
+ unless nil.respond_to? :blank?
7
+ def blank?
8
+ true
9
+ end
8
10
  end
9
11
 
10
12
  def to_crlf
@@ -1,13 +1,13 @@
1
1
  # encoding: utf-8
2
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
3
+ unless Object.method_defined? :blank?
4
+ class Object
5
+ def blank?
6
+ if respond_to?(:empty?)
7
+ empty?
8
+ else
9
+ !self
10
+ end
11
11
  end
12
12
  end
13
13
  end
@@ -5,21 +5,20 @@ module Net
5
5
  # http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&amp;revision=30294
6
6
  #
7
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)
8
+ begin
9
+ alias_method :original_tlsconnect, :tlsconnect
9
10
 
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)
11
+ def tlsconnect(s)
12
+ verified = false
13
+ begin
14
+ original_tlsconnect(s).tap { verified = true }
15
+ ensure
16
+ unless verified
17
+ s.close rescue nil
18
+ end
19
+ end
18
20
  end
19
- verified = true
20
- s
21
- ensure
22
- s.close unless verified
21
+ rescue NameError
23
22
  end
24
23
  end
25
24
  end
@@ -1,11 +1,24 @@
1
1
  # encoding: utf-8
2
2
  class String #:nodoc:
3
+
4
+ CRLF = "\r\n"
5
+ LF = "\n"
6
+
7
+ if RUBY_VERSION >= '1.9'
8
+ # This 1.9 only regex can save a reasonable amount of time (~20%)
9
+ # by not matching "\r\n" so the string is returned unchanged in
10
+ # the common case.
11
+ CRLF_REGEX = Regexp.new("(?<!\r)\n|\r(?!\n)")
12
+ else
13
+ CRLF_REGEX = /\n|\r\n|\r/
14
+ end
15
+
3
16
  def to_crlf
4
- to_str.gsub(/\n|\r\n|\r/) { "\r\n" }
17
+ to_str.gsub(CRLF_REGEX, CRLF)
5
18
  end
6
19
 
7
20
  def to_lf
8
- to_str.gsub(/\n|\r\n|\r/) { "\n" }
21
+ to_str.gsub(/\r\n|\r/, LF)
9
22
  end
10
23
 
11
24
  unless String.instance_methods(false).map {|m| m.to_sym}.include?(:blank?)
@@ -15,12 +28,12 @@ class String #:nodoc:
15
28
  end
16
29
 
17
30
  unless method_defined?(:ascii_only?)
18
- # Provides all strings with the Ruby 1.9 method of .ascii_only? and
19
- # returns true or false
20
- US_ASCII_REGEXP = %Q{\x00-\x7f}
31
+ # Backport from Ruby 1.9 checks for non-us-ascii characters.
21
32
  def ascii_only?
22
- !(self =~ /[^#{US_ASCII_REGEXP}]/)
33
+ self !~ MATCH_NON_US_ASCII
23
34
  end
35
+
36
+ MATCH_NON_US_ASCII = /[^\x00-\x7f]/
24
37
  end
25
38
 
26
39
  def not_ascii_only?
@@ -1,17 +1,17 @@
1
1
  # encoding: utf-8
2
2
  module Mail
3
3
  class Address
4
-
4
+
5
5
  include Mail::Utilities
6
-
6
+
7
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
8
+ # and parses it, breaking it down into its component parts and allowing you to get the
9
9
  # address, comments, display name, name, local part, domain part and fully formatted
10
10
  # address.
11
- #
11
+ #
12
12
  # Mail::Address requires a correctly formatted email address per RFC2822 or RFC822. It
13
13
  # handles all obsolete versions including obsolete domain routing on the local part.
14
- #
14
+ #
15
15
  # a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
16
16
  # a.format #=> 'Mikel Lindsaar <mikel@test.lindsaar.net> (My email address)'
17
17
  # a.address #=> 'mikel@test.lindsaar.net'
@@ -21,26 +21,23 @@ module Mail
21
21
  # a.comments #=> ['My email address']
22
22
  # a.to_s #=> 'Mikel Lindsaar <mikel@test.lindsaar.net> (My email address)'
23
23
  def initialize(value = nil)
24
- @output_type = nil
25
- @tree = nil
26
- @raw_text = value
27
- case
28
- when value.nil?
24
+ @output_type = :decode
25
+ if value.nil?
29
26
  @parsed = false
30
- return
27
+ @data = nil
31
28
  else
32
29
  parse(value)
33
30
  end
34
31
  end
35
-
36
- # Returns the raw imput of the passed in string, this is before it is passed
32
+
33
+ # Returns the raw input of the passed in string, this is before it is passed
37
34
  # by the parser.
38
35
  def raw
39
- @raw_text
36
+ @data.raw
40
37
  end
41
38
 
42
39
  # 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
40
+ # an incorrectly formatted address as input, Mail::Address will do its best
44
41
  # to format it correctly. This includes quoting display names as needed and
45
42
  # putting the address in angle brackets etc.
46
43
  #
@@ -48,37 +45,38 @@ module Mail
48
45
  # a.format #=> 'Mikel Lindsaar <mikel@test.lindsaar.net> (My email address)'
49
46
  def format
50
47
  parse unless @parsed
51
- case
52
- when tree.nil?
53
- ''
54
- when display_name
55
- [quote_phrase(display_name), "<#{address}>", format_comments].compact.join(" ")
48
+ if @data.nil?
49
+ EMPTY
50
+ elsif display_name
51
+ [quote_phrase(display_name), "<#{address}>", format_comments].compact.join(SPACE)
52
+ elsif address
53
+ [address, format_comments].compact.join(SPACE)
56
54
  else
57
- [address, format_comments].compact.join(" ")
55
+ raw
58
56
  end
59
57
  end
60
58
 
61
- # Returns the address that is in the address itself. That is, the
59
+ # Returns the address that is in the address itself. That is, the
62
60
  # local@domain string, without any angle brackets or the like.
63
- #
61
+ #
64
62
  # a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
65
63
  # a.address #=> 'mikel@test.lindsaar.net'
66
64
  def address
67
65
  parse unless @parsed
68
66
  domain ? "#{local}@#{domain}" : local
69
67
  end
70
-
68
+
71
69
  # Provides a way to assign an address to an already made Mail::Address object.
72
- #
70
+ #
73
71
  # a = Address.new
74
72
  # a.address = 'Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>'
75
73
  # a.address #=> 'mikel@test.lindsaar.net'
76
74
  def address=(value)
77
75
  parse(value)
78
76
  end
79
-
77
+
80
78
  # Returns the display name of the email address passed in.
81
- #
79
+ #
82
80
  # a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
83
81
  # a.display_name #=> 'Mikel Lindsaar'
84
82
  def display_name
@@ -86,9 +84,9 @@ module Mail
86
84
  @display_name ||= get_display_name
87
85
  Encodings.decode_encode(@display_name.to_s, @output_type) if @display_name
88
86
  end
89
-
87
+
90
88
  # Provides a way to assign a display name to an already made Mail::Address object.
91
- #
89
+ #
92
90
  # a = Address.new
93
91
  # a.address = 'mikel@test.lindsaar.net'
94
92
  # a.display_name = 'Mikel Lindsaar'
@@ -99,57 +97,53 @@ module Mail
99
97
 
100
98
  # Returns the local part (the left hand side of the @ sign in the email address) of
101
99
  # the address
102
- #
100
+ #
103
101
  # a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
104
102
  # a.local #=> 'mikel'
105
103
  def local
106
104
  parse unless @parsed
107
- "#{obs_domain_list}#{get_local.strip}" if get_local
105
+ "#{@data.obs_domain_list}#{get_local.strip}" if get_local
108
106
  end
109
107
 
110
108
  # Returns the domain part (the right hand side of the @ sign in the email address) of
111
109
  # the address
112
- #
110
+ #
113
111
  # a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
114
112
  # a.domain #=> 'test.lindsaar.net'
115
113
  def domain
116
114
  parse unless @parsed
117
115
  strip_all_comments(get_domain) if get_domain
118
116
  end
119
-
117
+
120
118
  # Returns an array of comments that are in the email, or an empty array if there
121
119
  # are no comments
122
- #
120
+ #
123
121
  # a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
124
122
  # a.comments #=> ['My email address']
125
123
  def comments
126
124
  parse unless @parsed
127
- if get_comments.empty?
128
- nil
129
- else
130
- get_comments.map { |c| c.squeeze(" ") }
131
- end
125
+ get_comments.map { |c| c.squeeze(SPACE) } unless get_comments.empty?
132
126
  end
133
-
127
+
134
128
  # Sometimes an address will not have a display name, but might have the name
135
129
  # as a comment field after the address. This returns that name if it exists.
136
- #
130
+ #
137
131
  # a = Address.new('mikel@test.lindsaar.net (Mikel Lindsaar)')
138
132
  # a.name #=> 'Mikel Lindsaar'
139
133
  def name
140
134
  parse unless @parsed
141
135
  get_name
142
136
  end
143
-
137
+
144
138
  # Returns the format of the address, or returns nothing
145
- #
139
+ #
146
140
  # a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
147
141
  # a.format #=> 'Mikel Lindsaar <mikel@test.lindsaar.net> (My email address)'
148
142
  def to_s
149
143
  parse unless @parsed
150
144
  format
151
145
  end
152
-
146
+
153
147
  # Shows the Address object basic details, including the Address
154
148
  # a = Address.new('Mikel (My email) <mikel@test.lindsaar.net>')
155
149
  # a.inspect #=> "#<Mail::Address:14184910 Address: |Mikel <mikel@test.lindsaar.net> (My email)| >"
@@ -157,48 +151,42 @@ module Mail
157
151
  parse unless @parsed
158
152
  "#<#{self.class}:#{self.object_id} Address: |#{to_s}| >"
159
153
  end
160
-
154
+
161
155
  def encoded
162
156
  @output_type = :encode
163
157
  format
164
158
  end
165
-
159
+
166
160
  def decoded
167
161
  @output_type = :decode
168
162
  format
169
163
  end
170
164
 
165
+ def group
166
+ @data && @data.group
167
+ end
168
+
171
169
  private
172
-
170
+
173
171
  def parse(value = nil)
174
172
  @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
173
+ @data = nil
174
+
175
+ case value
176
+ when Mail::Parsers::AddressStruct
177
+ @data = value
178
+ when String
179
+ unless value.blank?
180
+ address_list = Mail::Parsers::AddressListsParser.new.parse(value)
181
+ @data = address_list.addresses.first
182
+ end
195
183
  end
196
184
  end
197
185
 
198
186
  def strip_all_comments(string)
199
187
  unless comments.blank?
200
188
  comments.each do |comment|
201
- string = string.gsub("(#{comment})", '')
189
+ string = string.gsub("(#{comment})", EMPTY)
202
190
  end
203
191
  end
204
192
  string.strip
@@ -207,100 +195,53 @@ module Mail
207
195
  def strip_domain_comments(value)
208
196
  unless comments.blank?
209
197
  comments.each do |comment|
210
- if get_domain && get_domain.include?("(#{comment})")
211
- value = value.gsub("(#{comment})", '')
198
+ if @data.domain && @data.domain.include?("(#{comment})")
199
+ value = value.gsub("(#{comment})", EMPTY)
212
200
  end
213
201
  end
214
202
  end
215
203
  value.to_s.strip
216
204
  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
-
205
+
226
206
  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
207
+ if @data.display_name
208
+ str = strip_all_comments(@data.display_name.to_s)
209
+ elsif @data.comments && @data.domain
210
+ str = strip_domain_comments(format_comments)
244
211
  end
212
+
213
+ str unless str.blank?
245
214
  end
246
-
215
+
247
216
  def get_name
248
217
  if display_name
249
218
  str = display_name
250
- else
251
- if comments
252
- comment_text = comments.join(' ').squeeze(" ")
253
- str = "(#{comment_text})"
254
- end
219
+ elsif comments
220
+ str = "(#{comments.join(SPACE).squeeze(SPACE)})"
255
221
  end
256
222
 
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
223
+ unparen(str) unless str.blank?
267
224
  end
268
-
269
- def tree=(value)
270
- @tree = value
271
- end
272
-
225
+
273
226
  def format_comments
274
227
  if comments
275
- comment_text = comments.map {|c| escape_paren(c) }.join(' ').squeeze(" ")
228
+ comment_text = comments.map {|c| escape_paren(c) }.join(SPACE).squeeze(SPACE)
276
229
  @format_comments ||= "(#{comment_text})"
277
230
  else
278
231
  nil
279
232
  end
280
233
  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
-
234
+
291
235
  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
236
+ @data && @data.local
237
+ end
238
+
239
+ def get_domain
240
+ @data && @data.domain
241
+ end
242
+
243
+ def get_comments
244
+ @data && @data.comments
302
245
  end
303
-
304
-
305
246
  end
306
- end
247
+ end
@@ -1,7 +1,7 @@
1
1
  # encoding: utf-8
2
2
  module Mail
3
3
  class AddressList # :nodoc:
4
-
4
+
5
5
  # Mail::AddressList is the class that parses To, From and other address fields from
6
6
  # emails passed into Mail.
7
7
  #
@@ -18,57 +18,24 @@ module Mail
18
18
  # a.addresses #=> [#<Mail::Address:14943130 Address: |ada@test.lindsaar.net...
19
19
  # a.group_names #=> ["My Group"]
20
20
  def initialize(string)
21
- if string.blank?
22
- @address_nodes = []
23
- return self
24
- end
25
- parser = Mail::AddressListsParser.new
26
- if tree = parser.parse(string)
27
- @address_nodes = tree.addresses
28
- else
29
- raise Mail::Field::ParseError.new(AddressListsParser, string, parser.failure_reason)
30
- end
21
+ @addresses_grouped_by_group = nil
22
+ @address_list = Parsers::AddressListsParser.new.parse(string)
31
23
  end
32
24
 
33
25
  # Returns a list of address objects from the parsed line
34
26
  def addresses
35
- @addresses ||= get_addresses.map do |address_tree|
36
- Mail::Address.new(address_tree)
27
+ @addresses ||= @address_list.addresses.map do |address_data|
28
+ Mail::Address.new(address_data)
37
29
  end
38
30
  end
39
-
40
- # Returns a list of all recipient syntax trees that are not part of a group
41
- def individual_recipients # :nodoc:
42
- @individual_recipients ||= @address_nodes - group_recipients
43
- end
44
-
45
- # Returns a list of all recipient syntax trees that are part of a group
46
- def group_recipients # :nodoc:
47
- @group_recipients ||= @address_nodes.select { |an| an.respond_to?(:group_name) }
31
+
32
+ def addresses_grouped_by_group
33
+ addresses.select(&:group).group_by(&:group)
48
34
  end
49
35
 
50
36
  # Returns the names as an array of strings of all groups
51
37
  def group_names # :nodoc:
52
- group_recipients.map { |g| g.group_name.text_value }
53
- end
54
-
55
- # Returns a list of address syntax trees
56
- def address_nodes # :nodoc:
57
- @address_nodes
58
- end
59
-
60
- private
61
-
62
- def get_addresses
63
- (individual_recipients + group_recipients.map { |g| get_group_addresses(g) }).flatten
64
- end
65
-
66
- def get_group_addresses(g)
67
- if g.group_list.respond_to?(:addresses)
68
- g.group_list.addresses
69
- else
70
- []
71
- end
38
+ @address_list.group_names
72
39
  end
73
40
  end
74
41
  end
@@ -5,13 +5,9 @@ module Mail
5
5
  include Mail::Utilities
6
6
 
7
7
  def initialize( string )
8
- parser = Mail::ContentDispositionParser.new
9
- if tree = parser.parse(cleaned(string))
10
- @disposition_type = tree.disposition_type.text_value.downcase
11
- @parameters = tree.parameters
12
- else
13
- raise Mail::Field::ParseError.new(ContentDispositionElement, string, parser.failure_reason)
14
- end
8
+ content_disposition = Mail::Parsers::ContentDispositionParser.new.parse(cleaned(string))
9
+ @disposition_type = content_disposition.disposition_type
10
+ @parameters = content_disposition.parameters
15
11
  end
16
12
 
17
13
  def disposition_type
@@ -5,12 +5,8 @@ module Mail
5
5
  include Mail::Utilities
6
6
 
7
7
  def initialize( string )
8
- parser = Mail::ContentLocationParser.new
9
- if tree = parser.parse(string)
10
- @location = tree.location.text_value
11
- else
12
- raise Mail::Field::ParseError.new(ContentLocationElement, string, parser.failure_reason)
13
- end
8
+ content_location = Mail::Parsers::ContentLocationParser.new.parse(string)
9
+ @location = content_location.location
14
10
  end
15
11
 
16
12
  def location