* lib/uri/mailto.rb: update to latest specs, RFC 6068 and HTML5.

* lib/uri/mailto.rb (HEADER_PATTERN): removed.

* lib/uri/mailto.rb (HEADER_REGEXP): use RFC 6068 hfields.

* lib/uri/mailto.rb (EMAIL_REGEXP): use HTML5 email regexp.

* lib/uri/mailto.rb (URI::MailTo.build): support multiple to addresses.

* lib/uri/mailto.rb (URI::MailTo#initialize): Support multiple to
  addresses. Don't check with regexp, only split.

* lib/uri/mailto.rb (URI::MailTo#check_to): verify by matching
  URI path-rootless and HTML5 email regexp with unescaped one.

* lib/uri/mailto.rb (URI::MailTo#check_headers): verify only by
  HEADER_REGEXP.

* lib/uri/mailto.rb (URI::MailTo#set_headers): don't check by
  HEADER_REGEXP, only split it.

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@46590 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
naruse 2014-06-27 19:26:43 +00:00
parent 402d33fc44
commit e63ab5d3ad
3 changed files with 101 additions and 65 deletions

View File

@ -1,3 +1,27 @@
Sat Jun 28 04:08:22 2014 NARUSE, Yui <naruse@ruby-lang.org>
* lib/uri/mailto.rb: update to latest specs, RFC 6068 and HTML5.
* lib/uri/mailto.rb (HEADER_PATTERN): removed.
* lib/uri/mailto.rb (HEADER_REGEXP): use RFC 6068 hfields.
* lib/uri/mailto.rb (EMAIL_REGEXP): use HTML5 email regexp.
* lib/uri/mailto.rb (URI::MailTo.build): support multiple to addresses.
* lib/uri/mailto.rb (URI::MailTo#initialize): Support multiple to
addresses. Don't check with regexp, only split.
* lib/uri/mailto.rb (URI::MailTo#check_to): verify by matching
URI path-rootless and HTML5 email regexp with unescaped one.
* lib/uri/mailto.rb (URI::MailTo#check_headers): verify only by
HEADER_REGEXP.
* lib/uri/mailto.rb (URI::MailTo#set_headers): don't check by
HEADER_REGEXP, only split it.
Sat Jun 28 00:35:10 2014 Lauri Tirkkonen <lotheac@iki.fi> Sat Jun 28 00:35:10 2014 Lauri Tirkkonen <lotheac@iki.fi>
* tool/mkconfig.rb: fix empty RbConfig::CONFIG["prefix"] when * tool/mkconfig.rb: fix empty RbConfig::CONFIG["prefix"] when

View File

@ -12,7 +12,7 @@ require 'uri/generic'
module URI module URI
# #
# RFC2368, The mailto URL scheme # RFC6068, The mailto URL scheme
# #
class MailTo < Generic class MailTo < Generic
include REGEXP include REGEXP
@ -37,28 +37,22 @@ module URI
# #
# Within mailto URLs, the characters "?", "=", "&" are reserved. # Within mailto URLs, the characters "?", "=", "&" are reserved.
# hname = *urlc # ; RFC 6068
# hvalue = *urlc # hfields = "?" hfield *( "&" hfield )
# header = hname "=" hvalue # hfield = hfname "=" hfvalue
HEADER_PATTERN = "(?:[^?=&]*=[^?=&]*)".freeze # hfname = *qchar
HEADER_REGEXP = Regexp.new(HEADER_PATTERN).freeze # hfvalue = *qchar
# headers = "?" header *( "&" header ) # qchar = unreserved / pct-encoded / some-delims
# to = #mailbox # some-delims = "!" / "$" / "'" / "(" / ")" / "*"
# mailtoURL = "mailto:" [ to ] [ headers ] # / "+" / "," / ";" / ":" / "@"
MAILBOX_PATTERN = "(?:#{PATTERN::ESCAPED}|[^(),%?=&])".freeze #
MAILTO_REGEXP = Regexp.new(" # :nodoc: # ; RFC3986
\\A # unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
(#{MAILBOX_PATTERN}*?) (?# 1: to) # pct-encoded = "%" HEXDIG HEXDIG
(?: HEADER_REGEXP = /\A(?<hfield>(?:%\h\h|[!$'-.0-;@-Z_a-z~])*=(?:%\h\h|[!$'-.0-;@-Z_a-z~])*)(?:&\g<hfield>)*\z/
\\? # practical regexp for email address
(#{HEADER_PATTERN}(?:\\&#{HEADER_PATTERN})*) (?# 2: headers) # http://www.whatwg.org/specs/web-apps/current-work/multipage/states-of-the-type-attribute.html#valid-e-mail-address
)? EMAIL_REGEXP = /\A[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\z/
(?:
\\#
(#{PATTERN::FRAGMENT}) (?# 3: fragment)
)?
\\z
", Regexp::EXTENDED).freeze
# :startdoc: # :startdoc:
# #
@ -91,31 +85,35 @@ module URI
def self.build(args) def self.build(args)
tmp = Util::make_components_hash(self, args) tmp = Util::make_components_hash(self, args)
if tmp[:to] case tmp[:to]
tmp[:opaque] = tmp[:to] when Array
tmp[:opaque] = tmp[:to].join(',')
when String
tmp[:opaque] = tmp[:to].dup
else else
tmp[:opaque] = '' tmp[:opaque] = ''
end end
if tmp[:headers] if tmp[:headers]
tmp[:opaque] << '?' query =
case tmp[:headers]
if tmp[:headers].kind_of?(Array) when Array
tmp[:opaque] << tmp[:headers].collect { |x| tmp[:headers].collect { |x|
if x.kind_of?(Array) if x.kind_of?(Array)
x[0] + '=' + x[1..-1].join x[0] + '=' + x[1..-1].join
else else
x.to_s x.to_s
end end
}.join('&') }.join('&')
when Hash
elsif tmp[:headers].kind_of?(Hash) tmp[:headers].collect { |h,v|
tmp[:opaque] << tmp[:headers].collect { |h,v| h + '=' + v
h + '=' + v }.join('&')
}.join('&') else
tmp[:headers].to_s
else end
tmp[:opaque] << tmp[:headers].to_s unless query.empty?
tmp[:opaque] << '?' << query
end end
end end
@ -137,19 +135,23 @@ module URI
@to = nil @to = nil
@headers = [] @headers = []
if MAILTO_REGEXP =~ @opaque to, header = @opaque.split('?', 2)
if arg[10] # arg_check addrs = to.split(/[,;]/)
self.to = $1 # allow semicolon as a addr-spec separator
self.headers = $2 # http://support.microsoft.com/kb/820868
else
set_to($1)
set_headers($2)
end
else unless /\A(?:[^@,;]+@[^@,;]+(?:\z|[,;]))*\z/ =~ to
raise InvalidComponentError, raise InvalidComponentError,
"unrecognised opaque part for mailtoURL: #{@opaque}" "unrecognised opaque part for mailtoURL: #{@opaque}"
end end
if arg[10] # arg_check
self.to = to
self.headers = header
else
set_to(to)
set_headers(header)
end
end end
# The primary e-mail address of the URL, as a String # The primary e-mail address of the URL, as a String
@ -158,16 +160,25 @@ module URI
# E-mail headers set by the URL, as an Array of Arrays # E-mail headers set by the URL, as an Array of Arrays
attr_reader :headers attr_reader :headers
# check the to +v+ component against either # check the to +v+ component
# * URI::Parser Regexp for :OPAQUE
# * MAILBOX_PATTERN
def check_to(v) def check_to(v)
return true unless v return true unless v
return true if v.size == 0 return true if v.size == 0
if parser.regexp[:OPAQUE] !~ v || /\A#{MAILBOX_PATTERN}*\z/o !~ v v.split(/[,;]/).each do |addr|
raise InvalidComponentError, # check url safety as path-rootless
"bad component(expected opaque component): #{v}" if /\A(?:%\h\h|[!$&-.0-;=@-Z_a-z~])*\z/ !~ addr
raise InvalidComponentError,
"an address in 'to' is invalid as URI #{addr.dump}"
end
# check addr-spec
# don't s/\+/ /g
addr.gsub!(/%\h\h/, URI::TBLDECWWWCOMP_)
if EMAIL_REGEXP !~ addr
raise InvalidComponentError,
"an address in 'to' is invalid as uri-escaped addr-spec #{addr.dump}"
end
end end
return true return true
@ -188,14 +199,11 @@ module URI
end end
# check the headers +v+ component against either # check the headers +v+ component against either
# * URI::Parser Regexp for :OPAQUE # * HEADER_REGEXP
# * HEADER_PATTERN
def check_headers(v) def check_headers(v)
return true unless v return true unless v
return true if v.size == 0 return true if v.size == 0
if HEADER_REGEXP !~ v
if parser.regexp[:OPAQUE] !~ v ||
/\A(#{HEADER_PATTERN}(?:\&#{HEADER_PATTERN})*)\z/o !~ v
raise InvalidComponentError, raise InvalidComponentError,
"bad component(expected opaque component): #{v}" "bad component(expected opaque component): #{v}"
end end
@ -208,8 +216,8 @@ module URI
def set_headers(v) def set_headers(v)
@headers = [] @headers = []
if v if v
v.scan(HEADER_REGEXP) do |x| v.split('&').each do |x|
@headers << x.split(/=/o, 2) @headers << x.split(/=/, 2)
end end
end end
end end

View File

@ -26,6 +26,10 @@ class TestMailTo < Test::Unit::TestCase
ok[-1] << ["chris@example.com", nil] ok[-1] << ["chris@example.com", nil]
ok[-1] << {:to => "chris@example.com"} ok[-1] << {:to => "chris@example.com"}
ok << ["mailto:foo+@example.com,bar@example.com"]
ok[-1] << [["foo+@example.com", "bar@example.com"], nil]
ok[-1] << {:to => "foo+@example.com,bar@example.com"}
# mailto:infobot@example.com?subject=current-issue # mailto:infobot@example.com?subject=current-issue
ok << ["mailto:infobot@example.com?subject=current-issue"] ok << ["mailto:infobot@example.com?subject=current-issue"]
ok[-1] << ["infobot@example.com", ["subject=current-issue"]] ok[-1] << ["infobot@example.com", ["subject=current-issue"]]