kbaum-mail 2.1.2.1

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 (114) hide show
  1. data/CHANGELOG.rdoc +289 -0
  2. data/README.rdoc +575 -0
  3. data/Rakefile +72 -0
  4. data/TODO.rdoc +19 -0
  5. data/lib/mail.rb +113 -0
  6. data/lib/mail/attachments_list.rb +76 -0
  7. data/lib/mail/body.rb +243 -0
  8. data/lib/mail/configuration.rb +69 -0
  9. data/lib/mail/core_extensions/nil.rb +11 -0
  10. data/lib/mail/core_extensions/string.rb +19 -0
  11. data/lib/mail/elements/address.rb +306 -0
  12. data/lib/mail/elements/address_list.rb +74 -0
  13. data/lib/mail/elements/content_disposition_element.rb +30 -0
  14. data/lib/mail/elements/content_location_element.rb +25 -0
  15. data/lib/mail/elements/content_transfer_encoding_element.rb +21 -0
  16. data/lib/mail/elements/content_type_element.rb +35 -0
  17. data/lib/mail/elements/date_time_element.rb +26 -0
  18. data/lib/mail/elements/envelope_from_element.rb +34 -0
  19. data/lib/mail/elements/message_ids_element.rb +29 -0
  20. data/lib/mail/elements/mime_version_element.rb +26 -0
  21. data/lib/mail/elements/phrase_list.rb +21 -0
  22. data/lib/mail/elements/received_element.rb +30 -0
  23. data/lib/mail/encodings/base64.rb +18 -0
  24. data/lib/mail/encodings/encodings.rb +201 -0
  25. data/lib/mail/encodings/quoted_printable.rb +26 -0
  26. data/lib/mail/envelope.rb +35 -0
  27. data/lib/mail/field.rb +219 -0
  28. data/lib/mail/field_list.rb +33 -0
  29. data/lib/mail/fields/bcc_field.rb +53 -0
  30. data/lib/mail/fields/cc_field.rb +52 -0
  31. data/lib/mail/fields/comments_field.rb +41 -0
  32. data/lib/mail/fields/common/address_container.rb +16 -0
  33. data/lib/mail/fields/common/common_address.rb +128 -0
  34. data/lib/mail/fields/common/common_date.rb +51 -0
  35. data/lib/mail/fields/common/common_field.rb +64 -0
  36. data/lib/mail/fields/common/common_message_id.rb +57 -0
  37. data/lib/mail/fields/common/parameter_hash.rb +39 -0
  38. data/lib/mail/fields/content_description_field.rb +19 -0
  39. data/lib/mail/fields/content_disposition_field.rb +60 -0
  40. data/lib/mail/fields/content_id_field.rb +63 -0
  41. data/lib/mail/fields/content_location_field.rb +42 -0
  42. data/lib/mail/fields/content_transfer_encoding_field.rb +45 -0
  43. data/lib/mail/fields/content_type_field.rb +175 -0
  44. data/lib/mail/fields/date_field.rb +53 -0
  45. data/lib/mail/fields/from_field.rb +53 -0
  46. data/lib/mail/fields/in_reply_to_field.rb +52 -0
  47. data/lib/mail/fields/keywords_field.rb +43 -0
  48. data/lib/mail/fields/message_id_field.rb +80 -0
  49. data/lib/mail/fields/mime_version_field.rb +54 -0
  50. data/lib/mail/fields/optional_field.rb +11 -0
  51. data/lib/mail/fields/received_field.rb +62 -0
  52. data/lib/mail/fields/references_field.rb +53 -0
  53. data/lib/mail/fields/reply_to_field.rb +53 -0
  54. data/lib/mail/fields/resent_bcc_field.rb +53 -0
  55. data/lib/mail/fields/resent_cc_field.rb +53 -0
  56. data/lib/mail/fields/resent_date_field.rb +33 -0
  57. data/lib/mail/fields/resent_from_field.rb +53 -0
  58. data/lib/mail/fields/resent_message_id_field.rb +32 -0
  59. data/lib/mail/fields/resent_sender_field.rb +60 -0
  60. data/lib/mail/fields/resent_to_field.rb +53 -0
  61. data/lib/mail/fields/return_path_field.rb +62 -0
  62. data/lib/mail/fields/sender_field.rb +65 -0
  63. data/lib/mail/fields/structured_field.rb +36 -0
  64. data/lib/mail/fields/subject_field.rb +15 -0
  65. data/lib/mail/fields/to_field.rb +53 -0
  66. data/lib/mail/fields/unstructured_field.rb +117 -0
  67. data/lib/mail/header.rb +235 -0
  68. data/lib/mail/mail.rb +194 -0
  69. data/lib/mail/message.rb +1780 -0
  70. data/lib/mail/network/delivery_methods/file_delivery.rb +40 -0
  71. data/lib/mail/network/delivery_methods/sendmail.rb +62 -0
  72. data/lib/mail/network/delivery_methods/smtp.rb +110 -0
  73. data/lib/mail/network/delivery_methods/test_mailer.rb +40 -0
  74. data/lib/mail/network/retriever_methods/imap.rb +31 -0
  75. data/lib/mail/network/retriever_methods/pop3.rb +149 -0
  76. data/lib/mail/parsers/address_lists.rb +61 -0
  77. data/lib/mail/parsers/address_lists.treetop +19 -0
  78. data/lib/mail/parsers/content_disposition.rb +369 -0
  79. data/lib/mail/parsers/content_disposition.treetop +46 -0
  80. data/lib/mail/parsers/content_location.rb +133 -0
  81. data/lib/mail/parsers/content_location.treetop +20 -0
  82. data/lib/mail/parsers/content_transfer_encoding.rb +179 -0
  83. data/lib/mail/parsers/content_transfer_encoding.treetop +25 -0
  84. data/lib/mail/parsers/content_type.rb +512 -0
  85. data/lib/mail/parsers/content_type.treetop +58 -0
  86. data/lib/mail/parsers/date_time.rb +111 -0
  87. data/lib/mail/parsers/date_time.treetop +11 -0
  88. data/lib/mail/parsers/envelope_from.rb +188 -0
  89. data/lib/mail/parsers/envelope_from.treetop +32 -0
  90. data/lib/mail/parsers/message_ids.rb +42 -0
  91. data/lib/mail/parsers/message_ids.treetop +15 -0
  92. data/lib/mail/parsers/mime_version.rb +141 -0
  93. data/lib/mail/parsers/mime_version.treetop +19 -0
  94. data/lib/mail/parsers/phrase_lists.rb +42 -0
  95. data/lib/mail/parsers/phrase_lists.treetop +15 -0
  96. data/lib/mail/parsers/received.rb +68 -0
  97. data/lib/mail/parsers/received.treetop +11 -0
  98. data/lib/mail/parsers/rfc2045.rb +406 -0
  99. data/lib/mail/parsers/rfc2045.treetop +35 -0
  100. data/lib/mail/parsers/rfc2822.rb +5081 -0
  101. data/lib/mail/parsers/rfc2822.treetop +410 -0
  102. data/lib/mail/parsers/rfc2822_obsolete.rb +3607 -0
  103. data/lib/mail/parsers/rfc2822_obsolete.treetop +241 -0
  104. data/lib/mail/part.rb +82 -0
  105. data/lib/mail/parts_list.rb +34 -0
  106. data/lib/mail/patterns.rb +43 -0
  107. data/lib/mail/utilities.rb +163 -0
  108. data/lib/mail/vendor/treetop.rb +4 -0
  109. data/lib/mail/version.rb +10 -0
  110. data/lib/mail/version_specific/ruby_1_8.rb +84 -0
  111. data/lib/mail/version_specific/ruby_1_9.rb +77 -0
  112. data/lib/tasks/corpus.rake +125 -0
  113. data/lib/tasks/treetop.rake +10 -0
  114. metadata +188 -0
@@ -0,0 +1,69 @@
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 :test
39
+ Mail::TestMailer
40
+ else
41
+ method
42
+ end
43
+ end
44
+
45
+ def retriever_method(method = nil, settings = {})
46
+ return @retriever_method if @retriever_method && method.nil?
47
+ @retriever_method = lookup_retriever_method(method).new(settings)
48
+ end
49
+
50
+ def lookup_retriever_method(method)
51
+ case method
52
+ when nil
53
+ Mail::POP3
54
+ when :pop3
55
+ Mail::POP3
56
+ when :imap
57
+ Mail::IMAP
58
+ else
59
+ method
60
+ end
61
+ end
62
+
63
+ def param_encode_language(value = nil)
64
+ value ? @encode_language = value : @encode_language ||= 'en'
65
+ end
66
+
67
+ end
68
+
69
+ end
@@ -0,0 +1,11 @@
1
+ # encoding: utf-8
2
+
3
+ class NilClass #:nodoc:
4
+ def to_crlf
5
+ ''
6
+ end
7
+
8
+ def to_lf
9
+ ''
10
+ end
11
+ end
@@ -0,0 +1,19 @@
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
+ unless method_defined?(:ascii_only?)
12
+ # Provides all strings with the Ruby 1.9 method of .ascii_only? and
13
+ # returns true or false
14
+ US_ASCII_REGEXP = %Q{\x00-\x7f}
15
+ def ascii_only?
16
+ !(self =~ /[^#{US_ASCII_REGEXP}]/)
17
+ end
18
+ end
19
+ 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, @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}"
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)
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)
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) }
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)
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)
296
+ tree.angle_addr.addr_spec.local_part.text_value
297
+ when tree.respond_to?(:addr_spec)
298
+ tree.addr_spec.local_part.text_value
299
+ else
300
+ tree.local_part.text_value
301
+ end
302
+ end
303
+
304
+
305
+ end
306
+ end
@@ -0,0 +1,74 @@
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
+ 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, "AddressListsParser can not parse |#{string}|\nReason was: #{parser.failure_reason}\n"
30
+ end
31
+ end
32
+
33
+ # Returns a list of address objects from the parsed line
34
+ def addresses
35
+ @addresses ||= get_addresses.map do |address_tree|
36
+ Mail::Address.new(address_tree)
37
+ end
38
+ 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) }
48
+ end
49
+
50
+ # Returns the names as an array of strings of all groups
51
+ 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
72
+ end
73
+ end
74
+ end