Removed inadvertently added files from mspec.
[rbx.git] / lib / uri / mailto.rb
blob85891338a1dae2e17b9f6ad9e4ba8eceb04421fa
2 # = uri/mailto.rb
4 # Author:: Akira Yamada <akira@ruby-lang.org>
5 # License:: You can redistribute it and/or modify it under the same term as Ruby.
6 # Revision:: $Id: mailto.rb 14565 2007-12-24 01:51:49Z drbrain $
9 require 'uri/generic'
11 module URI
13   #
14   # RFC2368, The mailto URL scheme
15   #
16   class MailTo < Generic
17     include REGEXP
19     DEFAULT_PORT = nil
21     COMPONENT = [ :scheme, :to, :headers ].freeze
23     # :stopdoc:
24     #  "hname" and "hvalue" are encodings of an RFC 822 header name and
25     #  value, respectively. As with "to", all URL reserved characters must
26     #  be encoded.
27     #
28     #  "#mailbox" is as specified in RFC 822 [RFC822]. This means that it
29     #  consists of zero or more comma-separated mail addresses, possibly
30     #  including "phrase" and "comment" components. Note that all URL
31     #  reserved characters in "to" must be encoded: in particular,
32     #  parentheses, commas, and the percent sign ("%"), which commonly occur
33     #  in the "mailbox" syntax.
34     #
35     #  Within mailto URLs, the characters "?", "=", "&" are reserved.
37     # hname      =  *urlc
38     # hvalue     =  *urlc
39     # header     =  hname "=" hvalue
40     HEADER_PATTERN = "(?:[^?=&]*=[^?=&]*)".freeze
41     HEADER_REGEXP  = Regexp.new(HEADER_PATTERN, 'N').freeze
42     # headers    =  "?" header *( "&" header )
43     # to         =  #mailbox
44     # mailtoURL  =  "mailto:" [ to ] [ headers ]
45     MAILBOX_PATTERN = "(?:#{PATTERN::ESCAPED}|[^(),%?=&])".freeze
46     MAILTO_REGEXP = Regexp.new(" # :nodoc:
47       \\A
48       (#{MAILBOX_PATTERN}*?)                          (?# 1: to)
49       (?:
50         \\?
51         (#{HEADER_PATTERN}(?:\\&#{HEADER_PATTERN})*)  (?# 2: headers)
52       )?
53       (?:
54         \\#
55         (#{PATTERN::FRAGMENT})                        (?# 3: fragment)
56       )?
57       \\z
58     ", Regexp::EXTENDED).freeze
59     # :startdoc:
61     #
62     # == Description
63     #
64     # Creates a new URI::MailTo object from components, with syntax checking.
65     #
66     # Components can be provided as an Array or Hash. If an Array is used,
67     # the components must be supplied as [to, headers].
68     #
69     # If a Hash is used, the keys are the component names preceded by colons.
70     #
71     # The headers can be supplied as a pre-encoded string, such as 
72     # "subject=subscribe&cc=address", or as an Array of Arrays like
73     # [['subject', 'subscribe'], ['cc', 'address']]
74     #
75     # Examples:
76     # 
77     #    require 'uri'
78     #    
79     #    m1 = URI::MailTo.build(['joe@example.com', 'subject=Ruby'])
80     #    puts m1.to_s  ->  mailto:joe@example.com?subject=Ruby
81     #    
82     #    m2 = URI::MailTo.build(['john@example.com', [['Subject', 'Ruby'], ['Cc', 'jack@example.com']]])
83     #    puts m2.to_s  ->  mailto:john@example.com?Subject=Ruby&Cc=jack@example.com
84     #    
85     #    m3 = URI::MailTo.build({:to => 'listman@example.com', :headers => [['subject', 'subscribe']]})
86     #    puts m3.to_s  ->  mailto:listman@example.com?subject=subscribe
87     #
88     def self.build(args)
89       tmp = Util::make_components_hash(self, args)
91       if tmp[:to]
92         tmp[:opaque] = tmp[:to]
93       else
94         tmp[:opaque] = ''
95       end
97       if tmp[:headers]
98         tmp[:opaque] << '?'
100         if tmp[:headers].kind_of?(Array)
101           tmp[:opaque] << tmp[:headers].collect { |x|
102             if x.kind_of?(Array)
103               x[0] + '=' + x[1..-1].to_s
104             else
105               x.to_s
106             end
107           }.join('&')
109         elsif tmp[:headers].kind_of?(Hash)
110           tmp[:opaque] << tmp[:headers].collect { |h,v|
111             h + '=' + v
112           }.join('&')
114         else
115           tmp[:opaque] << tmp[:headers].to_s
116         end
117       end
119       return super(tmp)
120     end
122     #
123     # == Description
124     #
125     # Creates a new URI::MailTo object from generic URL components with
126     # no syntax checking.
127     #
128     # This method is usually called from URI::parse, which checks
129     # the validity of each component.
130     #
131     def initialize(*arg)
132       super(*arg)
134       @to = nil
135       @headers = []
137       if MAILTO_REGEXP =~ @opaque
138          if arg[-1]
139           self.to = $1
140           self.headers = $2
141         else
142           set_to($1)
143           set_headers($2)
144         end
146       else
147         raise InvalidComponentError,
148           "unrecognised opaque part for mailtoURL: #{@opaque}"
149       end
150     end
152     # The primary e-mail address of the URL, as a String
153     attr_reader :to
155     # E-mail headers set by the URL, as an Array of Arrays
156     attr_reader :headers
158     def check_to(v)
159       return true unless v
160       return true if v.size == 0
162       if OPAQUE !~ v || /\A#{MAILBOX_PATTERN}*\z/o !~ v
163         raise InvalidComponentError,
164           "bad component(expected opaque component): #{v}"
165       end
167       return true
168     end
169     private :check_to
171     def set_to(v)
172       @to = v
173     end
174     protected :set_to
176     def to=(v)
177       check_to(v)
178       set_to(v)
179       v
180     end
182     def check_headers(v)
183       return true unless v
184       return true if v.size == 0
186       if OPAQUE !~ v || 
187           /\A(#{HEADER_PATTERN}(?:\&#{HEADER_PATTERN})*)\z/o !~ v
188         raise InvalidComponentError,
189           "bad component(expected opaque component): #{v}"
190       end
192       return true
193     end
194     private :check_headers
196     def set_headers(v)
197       @headers = []
198       if v
199         v.scan(HEADER_REGEXP) do |x|
200           @headers << x.split(/=/o, 2)
201         end
202       end
203     end
204     protected :set_headers
206     def headers=(v)
207       check_headers(v)
208       set_headers(v)
209       v
210     end
212     def to_s
213       @scheme + ':' + 
214         if @to 
215           @to
216         else
217           ''
218         end + 
219         if @headers.size > 0
220           '?' + @headers.collect{|x| x.join('=')}.join('&')
221         else
222           ''
223         end +
224         if @fragment
225           '#' + @fragment
226         else
227           ''
228         end
229     end
230     
231     # Returns the RFC822 e-mail text equivalent of the URL, as a String.
232     #
233     # Example:
234     #
235     #   require 'uri'
236     #
237     #   uri = URI.parse("mailto:ruby-list@ruby-lang.org?Subject=subscribe&cc=myaddr")
238     #   uri.to_mailtext
239     #   # => "To: ruby-list@ruby-lang.org\nSubject: subscribe\nCc: myaddr\n\n\n"
240     #
241     def to_mailtext
242       to = URI::unescape(@to)
243       head = ''
244       body = ''
245       @headers.each do |x|
246         case x[0]
247         when 'body'
248           body = URI::unescape(x[1])
249         when 'to'
250           to << ', ' + URI::unescape(x[1])
251         else
252           head << URI::unescape(x[0]).capitalize + ': ' +
253             URI::unescape(x[1])  + "\n"
254         end
255       end
257       return "To: #{to}
258 #{head}
259 #{body}
261     end
262     alias to_rfc822text to_mailtext
263   end
265   @@schemes['MAILTO'] = MailTo