/tools/Ruby/lib/ruby/1.8/uri/mailto.rb

http://github.com/agross/netopenspace · Ruby · 266 lines · 152 code · 34 blank · 80 comment · 19 complexity · cf14d09406e2a0adcdb400143cb199b1 MD5 · raw file

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