Froze rails gems
[depot.git] / vendor / rails / actionmailer / lib / action_mailer / vendor / tmail-1.2.3 / tmail / address.rb
1 =begin rdoc
2
3 = Address handling class
4
5 =end
6 #--
7 # Copyright (c) 1998-2003 Minero Aoki <aamine@loveruby.net>
8 #
9 # Permission is hereby granted, free of charge, to any person obtaining
10 # a copy of this software and associated documentation files (the
11 # "Software"), to deal in the Software without restriction, including
12 # without limitation the rights to use, copy, modify, merge, publish,
13 # distribute, sublicense, and/or sell copies of the Software, and to
14 # permit persons to whom the Software is furnished to do so, subject to
15 # the following conditions:
16 #
17 # The above copyright notice and this permission notice shall be
18 # included in all copies or substantial portions of the Software.
19 #
20 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 #
28 # Note: Originally licensed under LGPL v2+. Using MIT license for Rails
29 # with permission of Minero Aoki.
30 #++
31
32 require 'tmail/encode'
33 require 'tmail/parser'
34
35
36 module TMail
37
38 # = Class Address
39 #
40 # Provides a complete handling library for email addresses. Can parse a string of an
41 # address directly or take in preformatted addresses themselves. Allows you to add
42 # and remove phrases from the front of the address and provides a compare function for
43 # email addresses.
44 #
45 # == Parsing and Handling a Valid Address:
46 #
47 # Just pass the email address in as a string to Address.parse:
48 #
49 # email = TMail::Address.parse('Mikel Lindsaar <mikel@lindsaar.net>)
50 # #=> #<TMail::Address mikel@lindsaar.net>
51 # email.address
52 # #=> "mikel@lindsaar.net"
53 # email.local
54 # #=> "mikel"
55 # email.domain
56 # #=> "lindsaar.net"
57 # email.name # Aliased as phrase as well
58 # #=> "Mikel Lindsaar"
59 #
60 # == Detecting an Invalid Address
61 #
62 # If you want to check the syntactical validity of an email address, just pass it to
63 # Address.parse and catch any SyntaxError:
64 #
65 # begin
66 # TMail::Mail.parse("mikel 2@@@@@ me .com")
67 # rescue TMail::SyntaxError
68 # puts("Invalid Email Address Detected")
69 # else
70 # puts("Address is valid")
71 # end
72 # #=> "Invalid Email Address Detected"
73 class Address
74
75 include TextUtils #:nodoc:
76
77 # Sometimes you need to parse an address, TMail can do it for you and provide you with
78 # a fairly robust method of detecting a valid address.
79 #
80 # Takes in a string, returns a TMail::Address object.
81 #
82 # Raises a TMail::SyntaxError on invalid email format
83 def Address.parse( str )
84 Parser.parse :ADDRESS, special_quote_address(str)
85 end
86
87 def Address.special_quote_address(str) #:nodoc:
88 # Takes a string which is an address and adds quotation marks to special
89 # edge case methods that the RACC parser can not handle.
90 #
91 # Right now just handles two edge cases:
92 #
93 # Full stop as the last character of the display name:
94 # Mikel L. <mikel@me.com>
95 # Returns:
96 # "Mikel L." <mikel@me.com>
97 #
98 # Unquoted @ symbol in the display name:
99 # mikel@me.com <mikel@me.com>
100 # Returns:
101 # "mikel@me.com" <mikel@me.com>
102 #
103 # Any other address not matching these patterns just gets returned as is.
104 case
105 # This handles the missing "" in an older version of Apple Mail.app
106 # around the display name when the display name contains a '@'
107 # like 'mikel@me.com <mikel@me.com>'
108 # Just quotes it to: '"mikel@me.com" <mikel@me.com>'
109 when str =~ /\A([^"].+@.+[^"])\s(<.*?>)\Z/
110 return "\"#{$1}\" #{$2}"
111 # This handles cases where 'Mikel A. <mikel@me.com>' which is a trailing
112 # full stop before the address section. Just quotes it to
113 # '"Mikel A. <mikel@me.com>"
114 when str =~ /\A(.*?\.)\s(<.*?>)\Z/
115 return "\"#{$1}\" #{$2}"
116 else
117 str
118 end
119 end
120
121 def address_group? #:nodoc:
122 false
123 end
124
125 # Address.new(local, domain)
126 #
127 # Accepts:
128 #
129 # * local - Left of the at symbol
130 #
131 # * domain - Array of the domain split at the periods.
132 #
133 # For example:
134 #
135 # Address.new("mikel", ["lindsaar", "net"])
136 # #=> "#<TMail::Address mikel@lindsaar.net>"
137 def initialize( local, domain )
138 if domain
139 domain.each do |s|
140 raise SyntaxError, 'empty word in domain' if s.empty?
141 end
142 end
143
144 # This is to catch an unquoted "@" symbol in the local part of the
145 # address. Handles addresses like <"@"@me.com> and makes sure they
146 # stay like <"@"@me.com> (previously were becoming <@@me.com>)
147 if local && (local.join == '@' || local.join =~ /\A[^"].*?@.*?[^"]\Z/)
148 @local = "\"#{local.join}\""
149 else
150 @local = local
151 end
152
153 @domain = domain
154 @name = nil
155 @routes = []
156 end
157
158 # Provides the name or 'phrase' of the email address.
159 #
160 # For Example:
161 #
162 # email = TMail::Address.parse("Mikel Lindsaar <mikel@lindsaar.net>")
163 # email.name
164 # #=> "Mikel Lindsaar"
165 def name
166 @name
167 end
168
169 # Setter method for the name or phrase of the email
170 #
171 # For Example:
172 #
173 # email = TMail::Address.parse("mikel@lindsaar.net")
174 # email.name
175 # #=> nil
176 # email.name = "Mikel Lindsaar"
177 # email.to_s
178 # #=> "Mikel Lindsaar <mikel@me.com>"
179 def name=( str )
180 @name = str
181 @name = nil if str and str.empty?
182 end
183
184 #:stopdoc:
185 alias phrase name
186 alias phrase= name=
187 #:startdoc:
188
189 # This is still here from RFC 822, and is now obsolete per RFC2822 Section 4.
190 #
191 # "When interpreting addresses, the route portion SHOULD be ignored."
192 #
193 # It is still here, so you can access it.
194 #
195 # Routes return the route portion at the front of the email address, if any.
196 #
197 # For Example:
198 # email = TMail::Address.parse( "<@sa,@another:Mikel@me.com>")
199 # => #<TMail::Address Mikel@me.com>
200 # email.to_s
201 # => "<@sa,@another:Mikel@me.com>"
202 # email.routes
203 # => ["sa", "another"]
204 def routes
205 @routes
206 end
207
208 def inspect #:nodoc:
209 "#<#{self.class} #{address()}>"
210 end
211
212 # Returns the local part of the email address
213 #
214 # For Example:
215 #
216 # email = TMail::Address.parse("mikel@lindsaar.net")
217 # email.local
218 # #=> "mikel"
219 def local
220 return nil unless @local
221 return '""' if @local.size == 1 and @local[0].empty?
222 # Check to see if it is an array before trying to map it
223 if @local.respond_to?(:map)
224 @local.map {|i| quote_atom(i) }.join('.')
225 else
226 quote_atom(@local)
227 end
228 end
229
230 # Returns the domain part of the email address
231 #
232 # For Example:
233 #
234 # email = TMail::Address.parse("mikel@lindsaar.net")
235 # email.local
236 # #=> "lindsaar.net"
237 def domain
238 return nil unless @domain
239 join_domain(@domain)
240 end
241
242 # Returns the full specific address itself
243 #
244 # For Example:
245 #
246 # email = TMail::Address.parse("mikel@lindsaar.net")
247 # email.address
248 # #=> "mikel@lindsaar.net"
249 def spec
250 s = self.local
251 d = self.domain
252 if s and d
253 s + '@' + d
254 else
255 s
256 end
257 end
258
259 alias address spec
260
261 # Provides == function to the email. Only checks the actual address
262 # and ignores the name/phrase component
263 #
264 # For Example
265 #
266 # addr1 = TMail::Address.parse("My Address <mikel@lindsaar.net>")
267 # #=> "#<TMail::Address mikel@lindsaar.net>"
268 # addr2 = TMail::Address.parse("Another <mikel@lindsaar.net>")
269 # #=> "#<TMail::Address mikel@lindsaar.net>"
270 # addr1 == addr2
271 # #=> true
272 def ==( other )
273 other.respond_to? :spec and self.spec == other.spec
274 end
275
276 alias eql? ==
277
278 # Provides a unique hash value for this record against the local and domain
279 # parts, ignores the name/phrase value
280 #
281 # email = TMail::Address.parse("mikel@lindsaar.net")
282 # email.hash
283 # #=> 18767598
284 def hash
285 @local.hash ^ @domain.hash
286 end
287
288 # Duplicates a TMail::Address object returning the duplicate
289 #
290 # addr1 = TMail::Address.parse("mikel@lindsaar.net")
291 # addr2 = addr1.dup
292 # addr1.id == addr2.id
293 # #=> false
294 def dup
295 obj = self.class.new(@local.dup, @domain.dup)
296 obj.name = @name.dup if @name
297 obj.routes.replace @routes
298 obj
299 end
300
301 include StrategyInterface #:nodoc:
302
303 def accept( strategy, dummy1 = nil, dummy2 = nil ) #:nodoc:
304 unless @local
305 strategy.meta '<>' # empty return-path
306 return
307 end
308
309 spec_p = (not @name and @routes.empty?)
310 if @name
311 strategy.phrase @name
312 strategy.space
313 end
314 tmp = spec_p ? '' : '<'
315 unless @routes.empty?
316 tmp << @routes.map {|i| '@' + i }.join(',') << ':'
317 end
318 tmp << self.spec
319 tmp << '>' unless spec_p
320 strategy.meta tmp
321 strategy.lwsp ''
322 end
323
324 end
325
326
327 class AddressGroup
328
329 include Enumerable
330
331 def address_group?
332 true
333 end
334
335 def initialize( name, addrs )
336 @name = name
337 @addresses = addrs
338 end
339
340 attr_reader :name
341
342 def ==( other )
343 other.respond_to? :to_a and @addresses == other.to_a
344 end
345
346 alias eql? ==
347
348 def hash
349 map {|i| i.hash }.hash
350 end
351
352 def []( idx )
353 @addresses[idx]
354 end
355
356 def size
357 @addresses.size
358 end
359
360 def empty?
361 @addresses.empty?
362 end
363
364 def each( &block )
365 @addresses.each(&block)
366 end
367
368 def to_a
369 @addresses.dup
370 end
371
372 alias to_ary to_a
373
374 def include?( a )
375 @addresses.include? a
376 end
377
378 def flatten
379 set = []
380 @addresses.each do |a|
381 if a.respond_to? :flatten
382 set.concat a.flatten
383 else
384 set.push a
385 end
386 end
387 set
388 end
389
390 def each_address( &block )
391 flatten.each(&block)
392 end
393
394 def add( a )
395 @addresses.push a
396 end
397
398 alias push add
399
400 def delete( a )
401 @addresses.delete a
402 end
403
404 include StrategyInterface
405
406 def accept( strategy, dummy1 = nil, dummy2 = nil )
407 strategy.phrase @name
408 strategy.meta ':'
409 strategy.space
410 first = true
411 each do |mbox|
412 if first
413 first = false
414 else
415 strategy.meta ','
416 end
417 strategy.space
418 mbox.accept strategy
419 end
420 strategy.meta ';'
421 strategy.lwsp ''
422 end
423
424 end
425
426 end # module TMail