summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornaruse <naruse@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2014-06-27 19:26:43 +0000
committernaruse <naruse@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2014-06-27 19:26:43 +0000
commite63ab5d3ad289767eab49787e4e33390b0ce74e1 (patch)
tree5b7b2c9d26553b8d43502d1c10a25b0d6098951e
parent402d33fc44b20779a90e9381cfd4f52154dfd872 (diff)
* 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
-rw-r--r--ChangeLog24
-rw-r--r--lib/uri/mailto.rb138
-rw-r--r--test/uri/test_mailto.rb4
3 files changed, 101 insertions, 65 deletions
diff --git a/ChangeLog b/ChangeLog
index 21e4e849b3..4c85cb6297 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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>
* tool/mkconfig.rb: fix empty RbConfig::CONFIG["prefix"] when
diff --git a/lib/uri/mailto.rb b/lib/uri/mailto.rb
index 03155fb917..325002de29 100644
--- a/lib/uri/mailto.rb
+++ b/lib/uri/mailto.rb
@@ -12,7 +12,7 @@ require 'uri/generic'
module URI
#
- # RFC2368, The mailto URL scheme
+ # RFC6068, The mailto URL scheme
#
class MailTo < Generic
include REGEXP
@@ -37,28 +37,22 @@ module URI
#
# Within mailto URLs, the characters "?", "=", "&" are reserved.
- # hname = *urlc
- # hvalue = *urlc
- # header = hname "=" hvalue
- HEADER_PATTERN = "(?:[^?=&]*=[^?=&]*)".freeze
- HEADER_REGEXP = Regexp.new(HEADER_PATTERN).freeze
- # headers = "?" header *( "&" header )
- # to = #mailbox
- # mailtoURL = "mailto:" [ to ] [ headers ]
- MAILBOX_PATTERN = "(?:#{PATTERN::ESCAPED}|[^(),%?=&])".freeze
- MAILTO_REGEXP = Regexp.new(" # :nodoc:
- \\A
- (#{MAILBOX_PATTERN}*?) (?# 1: to)
- (?:
- \\?
- (#{HEADER_PATTERN}(?:\\&#{HEADER_PATTERN})*) (?# 2: headers)
- )?
- (?:
- \\#
- (#{PATTERN::FRAGMENT}) (?# 3: fragment)
- )?
- \\z
- ", Regexp::EXTENDED).freeze
+ # ; RFC 6068
+ # hfields = "?" hfield *( "&" hfield )
+ # hfield = hfname "=" hfvalue
+ # hfname = *qchar
+ # hfvalue = *qchar
+ # qchar = unreserved / pct-encoded / some-delims
+ # some-delims = "!" / "$" / "'" / "(" / ")" / "*"
+ # / "+" / "," / ";" / ":" / "@"
+ #
+ # ; RFC3986
+ # unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
+ # 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
+ # 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/
# :startdoc:
#
@@ -91,31 +85,35 @@ module URI
def self.build(args)
tmp = Util::make_components_hash(self, args)
- if tmp[:to]
- tmp[:opaque] = tmp[:to]
+ case tmp[:to]
+ when Array
+ tmp[:opaque] = tmp[:to].join(',')
+ when String
+ tmp[:opaque] = tmp[:to].dup
else
tmp[:opaque] = ''
end
if tmp[:headers]
- tmp[:opaque] << '?'
-
- if tmp[:headers].kind_of?(Array)
- tmp[:opaque] << tmp[:headers].collect { |x|
- if x.kind_of?(Array)
- x[0] + '=' + x[1..-1].join
- else
- x.to_s
- end
- }.join('&')
-
- elsif tmp[:headers].kind_of?(Hash)
- tmp[:opaque] << tmp[:headers].collect { |h,v|
- h + '=' + v
- }.join('&')
-
- else
- tmp[:opaque] << tmp[:headers].to_s
+ query =
+ case tmp[:headers]
+ when Array
+ tmp[:headers].collect { |x|
+ if x.kind_of?(Array)
+ x[0] + '=' + x[1..-1].join
+ else
+ x.to_s
+ end
+ }.join('&')
+ when Hash
+ tmp[:headers].collect { |h,v|
+ h + '=' + v
+ }.join('&')
+ else
+ tmp[:headers].to_s
+ end
+ unless query.empty?
+ tmp[:opaque] << '?' << query
end
end
@@ -137,19 +135,23 @@ module URI
@to = nil
@headers = []
- if MAILTO_REGEXP =~ @opaque
- if arg[10] # arg_check
- self.to = $1
- self.headers = $2
- else
- set_to($1)
- set_headers($2)
- end
+ to, header = @opaque.split('?', 2)
+ addrs = to.split(/[,;]/)
+ # allow semicolon as a addr-spec separator
+ # http://support.microsoft.com/kb/820868
- else
+ unless /\A(?:[^@,;]+@[^@,;]+(?:\z|[,;]))*\z/ =~ to
raise InvalidComponentError,
"unrecognised opaque part for mailtoURL: #{@opaque}"
end
+
+ if arg[10] # arg_check
+ self.to = to
+ self.headers = header
+ else
+ set_to(to)
+ set_headers(header)
+ end
end
# 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
attr_reader :headers
- # check the to +v+ component against either
- # * URI::Parser Regexp for :OPAQUE
- # * MAILBOX_PATTERN
+ # check the to +v+ component
def check_to(v)
return true unless v
return true if v.size == 0
- if parser.regexp[:OPAQUE] !~ v || /\A#{MAILBOX_PATTERN}*\z/o !~ v
- raise InvalidComponentError,
- "bad component(expected opaque component): #{v}"
+ v.split(/[,;]/).each do |addr|
+ # check url safety as path-rootless
+ 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
return true
@@ -188,14 +199,11 @@ module URI
end
# check the headers +v+ component against either
- # * URI::Parser Regexp for :OPAQUE
- # * HEADER_PATTERN
+ # * HEADER_REGEXP
def check_headers(v)
return true unless v
return true if v.size == 0
-
- if parser.regexp[:OPAQUE] !~ v ||
- /\A(#{HEADER_PATTERN}(?:\&#{HEADER_PATTERN})*)\z/o !~ v
+ if HEADER_REGEXP !~ v
raise InvalidComponentError,
"bad component(expected opaque component): #{v}"
end
@@ -208,8 +216,8 @@ module URI
def set_headers(v)
@headers = []
if v
- v.scan(HEADER_REGEXP) do |x|
- @headers << x.split(/=/o, 2)
+ v.split('&').each do |x|
+ @headers << x.split(/=/, 2)
end
end
end
diff --git a/test/uri/test_mailto.rb b/test/uri/test_mailto.rb
index c2158845e8..661f7f7a9c 100644
--- a/test/uri/test_mailto.rb
+++ b/test/uri/test_mailto.rb
@@ -26,6 +26,10 @@ class TestMailTo < Test::Unit::TestCase
ok[-1] << ["chris@example.com", nil]
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
ok << ["mailto:infobot@example.com?subject=current-issue"]
ok[-1] << ["infobot@example.com", ["subject=current-issue"]]