mail 1.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of mail might be problematic. Click here for more details.

Files changed (107) hide show
  1. data/.gitignore +4 -0
  2. data/Manifest.txt +106 -0
  3. data/README.rdoc +441 -0
  4. data/Rakefile +38 -0
  5. data/lib/mail.rb +86 -0
  6. data/lib/mail/attachment.rb +90 -0
  7. data/lib/mail/body.rb +149 -0
  8. data/lib/mail/configuration.rb +90 -0
  9. data/lib/mail/core_extensions.rb +6 -0
  10. data/lib/mail/core_extensions/blank.rb +41 -0
  11. data/lib/mail/core_extensions/nil.rb +15 -0
  12. data/lib/mail/core_extensions/string.rb +31 -0
  13. data/lib/mail/elements/address.rb +293 -0
  14. data/lib/mail/elements/address_list.rb +62 -0
  15. data/lib/mail/elements/content_disposition_element.rb +34 -0
  16. data/lib/mail/elements/content_transfer_encoding_element.rb +21 -0
  17. data/lib/mail/elements/content_type_element.rb +39 -0
  18. data/lib/mail/elements/date_time_element.rb +26 -0
  19. data/lib/mail/elements/envelope_from_element.rb +34 -0
  20. data/lib/mail/elements/message_ids_element.rb +29 -0
  21. data/lib/mail/elements/mime_version_element.rb +26 -0
  22. data/lib/mail/elements/phrase_list.rb +21 -0
  23. data/lib/mail/elements/received_element.rb +30 -0
  24. data/lib/mail/encodings/base64.rb +17 -0
  25. data/lib/mail/encodings/encodings.rb +24 -0
  26. data/lib/mail/encodings/quoted_printable.rb +26 -0
  27. data/lib/mail/envelope.rb +35 -0
  28. data/lib/mail/field.rb +202 -0
  29. data/lib/mail/field_list.rb +33 -0
  30. data/lib/mail/fields/bcc_field.rb +40 -0
  31. data/lib/mail/fields/cc_field.rb +40 -0
  32. data/lib/mail/fields/comments_field.rb +41 -0
  33. data/lib/mail/fields/common/common_address.rb +62 -0
  34. data/lib/mail/fields/common/common_date.rb +35 -0
  35. data/lib/mail/fields/common/common_field.rb +128 -0
  36. data/lib/mail/fields/common/common_message_id.rb +35 -0
  37. data/lib/mail/fields/content_description_field.rb +15 -0
  38. data/lib/mail/fields/content_disposition_field.rb +34 -0
  39. data/lib/mail/fields/content_id_field.rb +50 -0
  40. data/lib/mail/fields/content_transfer_encoding_field.rb +28 -0
  41. data/lib/mail/fields/content_type_field.rb +50 -0
  42. data/lib/mail/fields/date_field.rb +44 -0
  43. data/lib/mail/fields/from_field.rb +40 -0
  44. data/lib/mail/fields/in_reply_to_field.rb +42 -0
  45. data/lib/mail/fields/keywords_field.rb +22 -0
  46. data/lib/mail/fields/message_id_field.rb +70 -0
  47. data/lib/mail/fields/mime_version_field.rb +42 -0
  48. data/lib/mail/fields/optional_field.rb +11 -0
  49. data/lib/mail/fields/received_field.rb +49 -0
  50. data/lib/mail/fields/references_field.rb +42 -0
  51. data/lib/mail/fields/reply_to_field.rb +40 -0
  52. data/lib/mail/fields/resent_bcc_field.rb +40 -0
  53. data/lib/mail/fields/resent_cc_field.rb +40 -0
  54. data/lib/mail/fields/resent_date_field.rb +16 -0
  55. data/lib/mail/fields/resent_from_field.rb +40 -0
  56. data/lib/mail/fields/resent_message_id_field.rb +20 -0
  57. data/lib/mail/fields/resent_sender_field.rb +48 -0
  58. data/lib/mail/fields/resent_to_field.rb +40 -0
  59. data/lib/mail/fields/return_path_field.rb +34 -0
  60. data/lib/mail/fields/sender_field.rb +48 -0
  61. data/lib/mail/fields/structured_field.rb +32 -0
  62. data/lib/mail/fields/subject_field.rb +14 -0
  63. data/lib/mail/fields/to_field.rb +40 -0
  64. data/lib/mail/fields/unstructured_field.rb +27 -0
  65. data/lib/mail/header.rb +213 -0
  66. data/lib/mail/mail.rb +120 -0
  67. data/lib/mail/message.rb +648 -0
  68. data/lib/mail/network/deliverable.rb +42 -0
  69. data/lib/mail/network/retrievable.rb +63 -0
  70. data/lib/mail/parsers/address_lists.rb +61 -0
  71. data/lib/mail/parsers/address_lists.treetop +19 -0
  72. data/lib/mail/parsers/content_disposition.rb +358 -0
  73. data/lib/mail/parsers/content_disposition.treetop +45 -0
  74. data/lib/mail/parsers/content_transfer_encoding.rb +179 -0
  75. data/lib/mail/parsers/content_transfer_encoding.treetop +25 -0
  76. data/lib/mail/parsers/content_type.rb +507 -0
  77. data/lib/mail/parsers/content_type.treetop +58 -0
  78. data/lib/mail/parsers/date_time.rb +111 -0
  79. data/lib/mail/parsers/date_time.treetop +11 -0
  80. data/lib/mail/parsers/envelope_from.rb +188 -0
  81. data/lib/mail/parsers/envelope_from.treetop +32 -0
  82. data/lib/mail/parsers/message_ids.rb +42 -0
  83. data/lib/mail/parsers/message_ids.treetop +15 -0
  84. data/lib/mail/parsers/mime_version.rb +141 -0
  85. data/lib/mail/parsers/mime_version.treetop +19 -0
  86. data/lib/mail/parsers/phrase_lists.rb +42 -0
  87. data/lib/mail/parsers/phrase_lists.treetop +15 -0
  88. data/lib/mail/parsers/received.rb +68 -0
  89. data/lib/mail/parsers/received.treetop +11 -0
  90. data/lib/mail/parsers/rfc2045.rb +406 -0
  91. data/lib/mail/parsers/rfc2045.treetop +35 -0
  92. data/lib/mail/parsers/rfc2822.rb +5005 -0
  93. data/lib/mail/parsers/rfc2822.treetop +402 -0
  94. data/lib/mail/parsers/rfc2822_obsolete.rb +3607 -0
  95. data/lib/mail/parsers/rfc2822_obsolete.treetop +241 -0
  96. data/lib/mail/part.rb +120 -0
  97. data/lib/mail/patterns.rb +42 -0
  98. data/lib/mail/utilities.rb +142 -0
  99. data/lib/mail/version.rb +10 -0
  100. data/lib/mail/version_specific/multibyte.rb +62 -0
  101. data/lib/mail/version_specific/multibyte/chars.rb +701 -0
  102. data/lib/mail/version_specific/multibyte/exceptions.rb +8 -0
  103. data/lib/mail/version_specific/multibyte/unicode_database.rb +71 -0
  104. data/lib/mail/version_specific/ruby_1_8.rb +61 -0
  105. data/lib/mail/version_specific/ruby_1_8_string.rb +88 -0
  106. data/lib/mail/version_specific/ruby_1_9.rb +49 -0
  107. metadata +192 -0
@@ -0,0 +1,6 @@
1
+ # encoding: utf-8
2
+ module Mail
3
+ require File.join(File.dirname(__FILE__), 'core_extensions/blank')
4
+ require File.join(File.dirname(__FILE__), 'core_extensions/nil')
5
+ require File.join(File.dirname(__FILE__), 'core_extensions/string')
6
+ end
@@ -0,0 +1,41 @@
1
+ # encoding: utf-8
2
+ #:nodoc:
3
+ # From the nice guys at rubyonrails.org -> ActiveSupport
4
+ class ::Object
5
+ # An object is blank if it's nil, empty, or a whitespace string.
6
+ # For example, "", " ", nil, [], and {} are blank.
7
+ #
8
+ # This simplifies
9
+ # if !address.nil? && !address.empty?
10
+ # to
11
+ # if !address.blank?
12
+ def blank?
13
+ respond_to?(:empty?) ? empty? : !self
14
+ end
15
+ end
16
+
17
+ class FalseClass #:nodoc:
18
+ def blank?
19
+ true
20
+ end
21
+ end
22
+
23
+ class TrueClass #:nodoc:
24
+ def blank?
25
+ false
26
+ end
27
+ end
28
+
29
+ class Array #:nodoc:
30
+ alias_method :blank?, :empty?
31
+ end
32
+
33
+ class Hash #:nodoc:
34
+ alias_method :blank?, :empty?
35
+ end
36
+
37
+ class Numeric #:nodoc:
38
+ def blank?
39
+ false
40
+ end
41
+ end
@@ -0,0 +1,15 @@
1
+ # encoding: utf-8
2
+
3
+ class NilClass #:nodoc:
4
+ def blank?
5
+ true
6
+ end
7
+
8
+ def to_crlf
9
+ ''
10
+ end
11
+
12
+ def to_lf
13
+ ''
14
+ end
15
+ end
@@ -0,0 +1,31 @@
1
+ # encoding: utf-8
2
+ class String #:nodoc:
3
+
4
+ if defined?(Mail::Multibyte)
5
+ include Mail::Multibyte
6
+ end
7
+
8
+ def blank?
9
+ self !~ /\S/
10
+ end
11
+
12
+ def to_crlf
13
+ self.gsub(/\n|\r\n|\r/) { "\r\n" }
14
+ end
15
+
16
+ def to_lf
17
+ self.gsub(/\n|\r\n|\r/) { "\n" }
18
+ end
19
+
20
+ if RUBY_VERSION <= "1.9"
21
+
22
+ # Provides all strings with the Ruby 1.9 method of .ascii_only? and
23
+ # returns true or false
24
+ US_ASCII_REGEXP = %Q{\x00-\x7f}
25
+ def ascii_only?
26
+ !(self =~ /[^#{US_ASCII_REGEXP}]/)
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,293 @@
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
+ @raw_text = value
25
+ case
26
+ when value.nil?
27
+ @parsed = false
28
+ return
29
+ else
30
+ parse(value)
31
+ end
32
+ end
33
+
34
+ # Returns the raw imput of the passed in string, this is before it is passed
35
+ # by the parser.
36
+ def raw
37
+ @raw_text
38
+ end
39
+
40
+ # Returns a correctly formatted address for the email going out. If given
41
+ # an incorrectly formatted address as input, Mail::Address will do it's best
42
+ # to format it correctly. This includes quoting display names as needed and
43
+ # putting the address in angle brackets etc.
44
+ #
45
+ # a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
46
+ # a.format #=> 'Mikel Lindsaar <mikel@test.lindsaar.net> (My email address)'
47
+ def format
48
+ parse unless @parsed
49
+ case
50
+ when tree.nil?
51
+ ''
52
+ when display_name
53
+ [quote_phrase(display_name), "<#{address}>", format_comments].compact.join(" ")
54
+ else
55
+ [address, format_comments].compact.join(" ")
56
+ end
57
+ end
58
+
59
+ # Returns the address that is in the address itself. That is, the
60
+ # local@domain string, without any angle brackets or the like.
61
+ #
62
+ # a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
63
+ # a.address #=> 'mikel@test.lindsaar.net'
64
+ def address
65
+ parse unless @parsed
66
+ domain ? "#{local}@#{domain}" : local
67
+ end
68
+
69
+ # Provides a way to assign an address to an already made Mail::Address object.
70
+ #
71
+ # a = Address.new
72
+ # a.address = 'Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>'
73
+ # a.address #=> 'mikel@test.lindsaar.net'
74
+ def address=(value)
75
+ parse(value)
76
+ end
77
+
78
+ # Returns the display name of the email address passed in.
79
+ #
80
+ # a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
81
+ # a.display_name #=> 'Mikel Lindsaar'
82
+ def display_name
83
+ parse unless @parsed
84
+ @display_name ||= get_display_name
85
+ end
86
+
87
+ # Provides a way to assign a display name to an already made Mail::Address object.
88
+ #
89
+ # a = Address.new
90
+ # a.address = 'mikel@test.lindsaar.net'
91
+ # a.display_name = 'Mikel Lindsaar'
92
+ # a.format #=> 'Mikel Lindsaar <mikel@test.lindsaar.net>'
93
+ def display_name=( str )
94
+ @display_name = str
95
+ end
96
+
97
+ # Returns the local part (the left hand side of the @ sign in the email address) of
98
+ # the address
99
+ #
100
+ # a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
101
+ # a.local #=> 'mikel'
102
+ def local
103
+ parse unless @parsed
104
+ "#{obs_domain_list}#{get_local.strip}"
105
+ end
106
+
107
+ # Returns the domain part (the right hand side of the @ sign in the email address) of
108
+ # the address
109
+ #
110
+ # a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
111
+ # a.domain #=> 'test.lindsaar.net'
112
+ def domain
113
+ parse unless @parsed
114
+ strip_all_comments(get_domain) if get_domain
115
+ end
116
+
117
+ # Returns an array of comments that are in the email, or an empty array if there
118
+ # are no comments
119
+ #
120
+ # a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
121
+ # a.comments #=> ['My email address']
122
+ def comments
123
+ parse unless @parsed
124
+ if get_comments.empty?
125
+ nil
126
+ else
127
+ get_comments.map { |c| c.squeeze(" ") }
128
+ end
129
+ end
130
+
131
+ # Sometimes an address will not have a display name, but might have the name
132
+ # as a comment field after the address. This returns that name if it exists.
133
+ #
134
+ # a = Address.new('mikel@test.lindsaar.net (Mikel Lindsaar)')
135
+ # a.name #=> 'Mikel Lindsaar'
136
+ def name
137
+ parse unless @parsed
138
+ get_name
139
+ end
140
+
141
+ # Returns the format of the address, or returns nothing
142
+ #
143
+ # a = Address.new('Mikel Lindsaar (My email address) <mikel@test.lindsaar.net>')
144
+ # a.format #=> 'Mikel Lindsaar <mikel@test.lindsaar.net> (My email address)'
145
+ def to_s
146
+ parse unless @parsed
147
+ format
148
+ end
149
+
150
+ # Shows the Address object basic details, including the Address
151
+ # a = Address.new('Mikel (My email) <mikel@test.lindsaar.net>')
152
+ # a.inspect #=> "#<Mail::Address:14184910 Address: |Mikel <mikel@test.lindsaar.net> (My email)| >"
153
+ def inspect
154
+ parse unless @parsed
155
+ "#<#{self.class}:#{self.object_id} Address: |#{to_s}| >"
156
+ end
157
+
158
+ private
159
+
160
+ def parse(value = nil)
161
+ @parsed = true
162
+ case
163
+ when value.nil?
164
+ nil
165
+ when value.class == String
166
+ self.tree = Mail::AddressList.new(value).address_nodes.first
167
+ else
168
+ self.tree = value
169
+ end
170
+ end
171
+
172
+
173
+ def get_domain
174
+ if tree.respond_to?(:angle_addr)
175
+ @domain_text ||= tree.angle_addr.addr_spec.domain.text_value.strip
176
+ elsif tree.respond_to?(:domain)
177
+ @domain_text ||= tree.domain.text_value.strip
178
+ elsif tree.respond_to?(:addr_spec)
179
+ tree.addr_spec.domain.text_value.strip
180
+ else
181
+ nil
182
+ end
183
+ end
184
+
185
+ def strip_all_comments(string)
186
+ unless comments.blank?
187
+ comments.each do |comment|
188
+ string = string.gsub("(#{comment})", '')
189
+ end
190
+ end
191
+ string.strip
192
+ end
193
+
194
+ def strip_domain_comments(value)
195
+ unless comments.blank?
196
+ comments.each do |comment|
197
+ if get_domain && get_domain.include?("(#{comment})")
198
+ value = value.gsub("(#{comment})", '')
199
+ end
200
+ end
201
+ end
202
+ value.to_s.strip
203
+ end
204
+
205
+ def get_comments
206
+ if tree.respond_to?(:comments)
207
+ @comments ||= tree.comments.map { |c| unparen(c.text_value) }
208
+ else
209
+ @comments = []
210
+ end
211
+ end
212
+
213
+ def get_display_name
214
+ if tree.respond_to?(:display_name)
215
+ name = unquote(tree.display_name.text_value.strip)
216
+ str = strip_all_comments(name)
217
+ elsif comments
218
+ if domain
219
+ str = strip_domain_comments(format_comments)
220
+ else
221
+ str = nil
222
+ end
223
+ else
224
+ nil
225
+ end
226
+
227
+ if str.blank?
228
+ nil
229
+ else
230
+ str
231
+ end
232
+ end
233
+
234
+ def get_name
235
+ if display_name
236
+ str = display_name
237
+ else
238
+ if comments
239
+ comment_text = comments.join(' ').squeeze(" ")
240
+ str = "(#{comment_text})"
241
+ end
242
+ end
243
+
244
+ if str.blank?
245
+ nil
246
+ else
247
+ unparen(str)
248
+ end
249
+ end
250
+
251
+ # Provides access to the Treetop parse tree for this address
252
+ def tree
253
+ @tree
254
+ end
255
+
256
+ def tree=(value)
257
+ @tree = value
258
+ end
259
+
260
+ def format_comments
261
+ if comments
262
+ comment_text = comments.map {|c| escape_paren(c) }.join(' ').squeeze(" ")
263
+ @format_comments ||= "(#{comment_text})"
264
+ else
265
+ nil
266
+ end
267
+ end
268
+
269
+ def obs_domain_list
270
+ if tree.respond_to?(:angle_addr)
271
+ obs = tree.angle_addr.elements.select { |e| e.respond_to?(:obs_domain_list) }
272
+ !obs.empty? ? obs.first.text_value : nil
273
+ else
274
+ nil
275
+ end
276
+ end
277
+
278
+ def get_local
279
+ case
280
+ when tree.respond_to?(:local_dot_atom_text)
281
+ tree.local_dot_atom_text.text_value
282
+ when tree.respond_to?(:angle_addr)
283
+ tree.angle_addr.addr_spec.local_part.text_value
284
+ when tree.respond_to?(:addr_spec)
285
+ tree.addr_spec.local_part.text_value
286
+ else
287
+ tree.local_part.text_value
288
+ end
289
+ end
290
+
291
+
292
+ end
293
+ end
@@ -0,0 +1,62 @@
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
+ parser = Mail::AddressListsParser.new
22
+ if tree = parser.parse(string)
23
+ @address_nodes = tree.addresses
24
+ else
25
+ raise Mail::Field::ParseError, "AddressListsParser can not parse |#{string}|\nReason was: #{parser.failure_reason}\n"
26
+ end
27
+ end
28
+
29
+ # Returns a list of address objects from the parsed line
30
+ def addresses
31
+ @addresses ||= get_addresses.map do |address_tree|
32
+ Mail::Address.new(address_tree)
33
+ end
34
+ end
35
+
36
+ # Returns a list of all recipient syntax trees that are not part of a group
37
+ def individual_recipients # :nodoc:
38
+ @individual_recipients ||= @address_nodes - group_recipients
39
+ end
40
+
41
+ # Returns a list of all recipient syntax trees that are part of a group
42
+ def group_recipients # :nodoc:
43
+ @group_recipients ||= @address_nodes.select { |an| an.respond_to?(:group_name) }
44
+ end
45
+
46
+ # Returns the names as an array of strings of all groups
47
+ def group_names # :nodoc:
48
+ group_recipients.map { |g| g.group_name.text_value }
49
+ end
50
+
51
+ # Returns a list of address syntax trees
52
+ def address_nodes # :nodoc:
53
+ @address_nodes
54
+ end
55
+
56
+ private
57
+
58
+ def get_addresses
59
+ (individual_recipients + group_recipients.map { |g| g.group_list.addresses }).flatten
60
+ end
61
+ end
62
+ end