summaryrefslogtreecommitdiff
path: root/lib/uri
diff options
context:
space:
mode:
authorakira <akira@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2002-01-10 08:00:51 +0000
committerakira <akira@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2002-01-10 08:00:51 +0000
commit8a277783abde4824ab9f92a083dc6221c5fa4098 (patch)
tree67820073efcecd2ab006fff86b72c4f5020a7682 /lib/uri
parentd92e9de3b862af630aea7583bd4d45709c5d9085 (diff)
added uri library (uri-0.9.4)
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@1979 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib/uri')
-rw-r--r--lib/uri/common.rb419
-rw-r--r--lib/uri/ftp.rb149
-rw-r--r--lib/uri/generic.rb1087
-rw-r--r--lib/uri/http.rb76
-rw-r--r--lib/uri/https.rb26
-rw-r--r--lib/uri/ldap.rb238
-rw-r--r--lib/uri/mailto.rb260
7 files changed, 2255 insertions, 0 deletions
diff --git a/lib/uri/common.rb b/lib/uri/common.rb
new file mode 100644
index 0000000000..ed2f6a7a5b
--- /dev/null
+++ b/lib/uri/common.rb
@@ -0,0 +1,419 @@
+#
+# $Id$
+#
+# Copyright (c) 2001 akira yamada <akira@ruby-lang.org>
+# You can redistribute it and/or modify it under the same term as Ruby.
+#
+
+=begin
+
+== URI
+
+=end
+
+module URI
+ module REGEXP
+ module PATTERN
+ # RFC 2396 (URI Generic Syntax)
+ # RFC 2732 (IPv6 Literal Addresses in URL's)
+ # RFC 2373 (IPv6 Addressing Architecture)
+
+ # alpha = lowalpha | upalpha
+ ALPHA = "a-zA-Z"
+ # alphanum = alpha | digit
+ ALNUM = "#{ALPHA}\\d"
+
+ # hex = digit | "A" | "B" | "C" | "D" | "E" | "F" |
+ # "a" | "b" | "c" | "d" | "e" | "f"
+ HEX = "a-fA-F\\d"
+ # escaped = "%" hex hex
+ ESCAPED = "%[#{HEX}]{2}"
+ # mark = "-" | "_" | "." | "!" | "~" | "*" | "'" |
+ # "(" | ")"
+ # unreserved = alphanum | mark
+ UNRESERVED = "-_.!~*'()#{ALNUM}"
+ # reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" |
+ # "$" | ","
+ # reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" |
+ # "$" | "," | "[" | "]" (RFC 2732)
+ RESERVED = ";/?:@&=+$,\\[\\]"
+
+ # uric = reserved | unreserved | escaped
+ URIC = "(?:[#{UNRESERVED}#{RESERVED}]|#{ESCAPED})"
+ # uric_no_slash = unreserved | escaped | ";" | "?" | ":" | "@" |
+ # "&" | "=" | "+" | "$" | ","
+ URIC_NO_SLASH = "(?:[#{UNRESERVED};?:@&=+$,]|#{ESCAPED})"
+ # query = *uric
+ QUERY = "#{URIC}*"
+ # fragment = *uric
+ FRAGMENT = "#{URIC}*"
+
+ # domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
+ DOMLABEL = "(?:[#{ALNUM}](?:[-#{ALNUM}]*[#{ALNUM}])?)"
+ # toplabel = alpha | alpha *( alphanum | "-" ) alphanum
+ TOPLABEL = "(?:[#{ALPHA}](?:[-#{ALNUM}]*[#{ALNUM}])?)"
+ # hostname = *( domainlabel "." ) toplabel [ "." ]
+ HOSTNAME = "(?:#{DOMLABEL}\\.)*#{TOPLABEL}\\.?"
+
+ # RFC 2373, APPENDIX B:
+ # IPv6address = hexpart [ ":" IPv4address ]
+ # IPv4address = 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT
+ # hexpart = hexseq | hexseq "::" [ hexseq ] | "::" [ hexseq ]
+ # hexseq = hex4 *( ":" hex4)
+ # hex4 = 1*4HEXDIG
+ #
+ # XXX: This definition has a flaw. "::" + IPv4address must be
+ # allowed too. Here is a replacement.
+ #
+ # IPv4address = 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT "." 1*3DIGIT
+ IPV4ADDR = "\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}"
+ # hex4 = 1*4HEXDIG
+ HEX4 = "[#{HEX}]{1,4}"
+ # lastpart = hex4 | IPv4address
+ LASTPART = "(?:#{HEX4}|#{IPV4ADDR})"
+ # hexseq1 = *( hex4 ":" ) hex4
+ HEXSEQ1 = "(?:#{HEX4}:)*#{HEX4}"
+ # hexseq2 = *( hex4 ":" ) lastpart
+ HEXSEQ2 = "(?:#{HEX4}:)*#{LASTPART}"
+ # IPv6address = hexseq2 | [ hexseq1 ] "::" [ hexseq2 ]
+ IPV6ADDR = "(?:#{HEXSEQ2}|(?:#{HEXSEQ1})?::(?:#{HEXSEQ2})?)"
+
+ # IPv6prefix = ( hexseq1 | [ hexseq1 ] "::" [ hexseq1 ] ) "/" 1*2DIGIT
+ # unused
+
+ # ipv6reference = "[" IPv6address "]" (RFC 2732)
+ IPV6REF = "\\[#{IPV6ADDR}\\]"
+
+ # host = hostname | IPv4address
+ # host = hostname | IPv4address | IPv6reference (RFC 2732)
+ HOST = "(?:#{HOSTNAME}|#{IPV4ADDR}|#{IPV6REF})"
+ # port = *digit
+ PORT = "\d*"
+ # hostport = host [ ":" port ]
+ HOSTPORT = "#{HOST}(?:#{PORT})?"
+
+ # userinfo = *( unreserved | escaped |
+ # ";" | ":" | "&" | "=" | "+" | "$" | "," )
+ USERINFO = "(?:[#{UNRESERVED};:&=+$,]|#{ESCAPED})*"
+
+ # pchar = unreserved | escaped |
+ # ":" | "@" | "&" | "=" | "+" | "$" | ","
+ PCHAR = "(?:[#{UNRESERVED}:@&=+$,]|#{ESCAPED})"
+ # param = *pchar
+ PARAM = "#{PCHAR}*"
+ # segment = *pchar *( ";" param )
+ SEGMENT = "#{PCHAR}*(?:;#{PARAM})*"
+ # path_segments = segment *( "/" segment )
+ PATH_SEGMENTS = "#{SEGMENT}(?:/#{SEGMENT})*"
+
+ # server = [ [ userinfo "@" ] hostport ]
+ SERVER = "(?:#{USERINFO}@)?#{HOSTPORT}"
+ # reg_name = 1*( unreserved | escaped | "$" | "," |
+ # ";" | ":" | "@" | "&" | "=" | "+" )
+ REG_NAME = "(?:[#{UNRESERVED}$,;+@&=+]|#{ESCAPED})+"
+ # authority = server | reg_name
+ AUTHORITY = "(?:#{SERVER}|#{REG_NAME})"
+
+ # rel_segment = 1*( unreserved | escaped |
+ # ";" | "@" | "&" | "=" | "+" | "$" | "," )
+ REL_SEGMENT = "(?:[#{UNRESERVED};@&=+$,]|#{ESCAPED})+"
+
+ # scheme = alpha *( alpha | digit | "+" | "-" | "." )
+ SCHEME = "[#{ALPHA}][-+.#{ALPHA}\\d]*"
+
+ # abs_path = "/" path_segments
+ ABS_PATH = "/#{PATH_SEGMENTS}"
+ # rel_path = rel_segment [ abs_path ]
+ REL_PATH = "#{REL_SEGMENT}(?:#{ABS_PATH})?"
+ # net_path = "//" authority [ abs_path ]
+ NET_PATH = "//#{AUTHORITY}(?:#{ABS_PATH})?"
+
+ # hier_part = ( net_path | abs_path ) [ "?" query ]
+ HIER_PART = "(?:#{NET_PATH}|#{ABS_PATH})(?:\\?(?:#{QUERY}))?"
+ # opaque_part = uric_no_slash *uric
+ OPAQUE_PART = "#{URIC_NO_SLASH}#{URIC}*"
+
+ # absoluteURI = scheme ":" ( hier_part | opaque_part )
+ ABS_URI = "#{SCHEME}:(?:#{HIER_PART}|#{OPAQUE_PART})"
+ # relativeURI = ( net_path | abs_path | rel_path ) [ "?" query ]
+ REL_URI = "(?:#{NET_PATH}|#{ABS_PATH}|#{REL_PATH})(?:\\?#{QUERY})?"
+
+ # URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ]
+ URI_REF = "(?:#{ABS_URI}|#{REL_URI})?(?:##{FRAGMENT})?"
+
+ # XXX:
+ X_ABS_URI = "
+ (#{PATTERN::SCHEME}): (?# 1: scheme)
+ (?:
+ (?:
+ //(?:
+ (?:(?:(#{PATTERN::USERINFO})@)? (?# 2: userinfo)
+ (?:(#{PATTERN::HOST})(?::(\\d*))?))?(?# 3: host, 4: port)
+ |
+ (#{PATTERN::REG_NAME}) (?# 5: registry)
+ )
+ (#{PATTERN::ABS_PATH})? (?# 6: path)
+ )(?:\\?(#{PATTERN::QUERY}))? (?# 7: query)
+ |
+ (#{PATTERN::OPAQUE_PART}) (?# 8: opaque)
+ )
+ (?:\\#(#{PATTERN::FRAGMENT}))? (?# 9: fragment)
+ "
+ X_REL_URI = "
+ (?:
+ (?:
+ //
+ (?:
+ (?:(#{PATTERN::USERINFO})@)? (?# 1: userinfo)
+ (#{PATTERN::HOST})?(?::(\\d*))? (?# 2: host, 3: port)
+ |
+ (#{PATTERN::REG_NAME}) (?# 4: registry)
+ )
+ )
+ |
+ (#{PATTERN::REL_SEGMENT}) (?# 5: rel_segment)
+ )?
+ (#{PATTERN::ABS_PATH})? (?# 6: abs_path)
+ (?:\\?(#{PATTERN::QUERY}))? (?# 7: query)
+ (?:\\#(#{PATTERN::FRAGMENT}))? (?# 8: fragment)
+ "
+ end # PATTERN
+
+ # for URI::split
+ ABS_URI = Regexp.new('^' + PATTERN::X_ABS_URI + '$', #'
+ Regexp::EXTENDED, 'N').freeze
+ REL_URI = Regexp.new('^' + PATTERN::X_REL_URI + '$', #'
+ Regexp::EXTENDED, 'N').freeze
+
+ # for URI::extract
+ URI_REF = Regexp.new(PATTERN::URI_REF, false, 'N').freeze
+ ABS_URI_REF = Regexp.new(PATTERN::X_ABS_URI, Regexp::EXTENDED, 'N').freeze
+ REL_URI_REF = Regexp.new(PATTERN::X_REL_URI, Regexp::EXTENDED, 'N').freeze
+
+ # for URI::escape/unescape
+ ESCAPED = Regexp.new(PATTERN::ESCAPED, false, 'N').freeze
+ UNSAFE = Regexp.new("[^#{PATTERN::UNRESERVED}#{PATTERN::RESERVED}]",
+ false, 'N').freeze
+
+ # for Generic#initialize
+ SCHEME = Regexp.new("^#{PATTERN::SCHEME}$", false, 'N').freeze #"
+ USERINFO = Regexp.new("^#{PATTERN::USERINFO}$", false, 'N').freeze #"
+ HOST = Regexp.new("^#{PATTERN::HOST}$", false, 'N').freeze #"
+ PORT = Regexp.new("^#{PATTERN::PORT}$", false, 'N').freeze #"
+ OPAQUE = Regexp.new("^#{PATTERN::OPAQUE_PART}$", false, 'N').freeze #"
+ REGISTRY = Regexp.new("^#{PATTERN::REG_NAME}$", false, 'N').freeze #"
+ ABS_PATH = Regexp.new("^#{PATTERN::ABS_PATH}$", false, 'N').freeze #"
+ REL_PATH = Regexp.new("^#{PATTERN::REL_PATH}$", false, 'N').freeze #"
+ QUERY = Regexp.new("^#{PATTERN::QUERY}$", false, 'N').freeze #"
+ FRAGMENT = Regexp.new("^#{PATTERN::FRAGMENT}$", false, 'N').freeze #"
+ end # REGEXP
+
+ module Util
+ def make_components_hash(klass, array_hash)
+ tmp = {}
+ if array_hash.kind_of?(Array) &&
+ array_hash.size == klass.component.size - 1
+ klass.component[1..-1].each_index do |i|
+ begin
+ tmp[klass.component[i + 1]] = array_hash[i].clone
+ rescue TypeError
+ tmp[klass.component[i + 1]] = array_hash[i]
+ end
+ end
+
+ elsif array_hash.kind_of?(Hash)
+ array_hash.each do |key, value|
+ begin
+ tmp[key] = value.clone
+ rescue TypeError
+ tmp[key] = value
+ end
+ end
+ else
+ raise ArgumentError,
+ "expected Array of or Hash of compornents of #{klass.to_s} (#{klass.component[1..-1].join(', ')})"
+ end
+ tmp[:scheme] = klass.to_s.sub(/\A.*::/, '').downcase
+
+ return tmp
+ end
+ module_function :make_components_hash
+ end
+
+ module Escape
+ include REGEXP
+
+ def escape(str, unsafe = UNSAFE)
+ unless unsafe.kind_of?(Regexp)
+ # perhaps unsafe is String object
+ unsafe = Regexp.new(Regexp.quote(unsafe), false, 'N')
+ end
+ str.gsub(unsafe) do |us|
+ tmp = ''
+ us.each_byte do |uc|
+ tmp << sprintf('%%%02X', uc)
+ end
+ tmp
+ end
+ end
+ alias encode escape
+
+ def unescape(str)
+ str.gsub(ESCAPED) do
+ $&[1,2].hex.chr
+ end
+ end
+ alias decode unescape
+ end
+
+ include REGEXP
+ extend Escape
+
+ @@schemes = {}
+
+ class Error < StandardError; end
+ class InvalidURIError < Error; end # it is not URI.
+ class InvalidComponentError < Error; end # it is not component of URI.
+ class BadURIError < Error; end # the URI is valid but it is bad for the position.
+
+=begin
+
+=== Methods
+
+--- URI::split(uri)
+
+=end
+
+ def self.split(uri)
+ case uri
+ when ''
+ # null uri
+
+ when ABS_URI
+ scheme, userinfo, host, port,
+ registry, path, query, opaque, fragment = $~[1..-1]
+
+ # URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ]
+
+ # absoluteURI = scheme ":" ( hier_part | opaque_part )
+ # hier_part = ( net_path | abs_path ) [ "?" query ]
+ # opaque_part = uric_no_slash *uric
+
+ # abs_path = "/" path_segments
+ # net_path = "//" authority [ abs_path ]
+
+ # authority = server | reg_name
+ # server = [ [ userinfo "@" ] hostport ]
+
+ if !scheme
+ raise InvalidURIError,
+ "bad URI(absolute but no scheme): #{uri}"
+ end
+ if !opaque && (!path && (!host && !registry))
+ raise InvalidURIError,
+ "bad URI(absolute but no path): #{uri}"
+ end
+
+ when REL_URI
+ scheme = nil
+ opaque = nil
+
+ userinfo, host, port, registry,
+ rel_segment, abs_path, query, fragment = $~[1..-1]
+ if rel_segment && abs_path
+ path = rel_segment + abs_path
+ elsif rel_segment
+ path = rel_segment
+ elsif abs_path
+ path = abs_path
+ end
+
+ # URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ]
+
+ # relativeURI = ( net_path | abs_path | rel_path ) [ "?" query ]
+
+ # net_path = "//" authority [ abs_path ]
+ # abs_path = "/" path_segments
+ # rel_path = rel_segment [ abs_path ]
+
+ # authority = server | reg_name
+ # server = [ [ userinfo "@" ] hostport ]
+
+ else
+ raise InvalidURIError, "bad URI(is not URI?): #{uri}"
+ end
+
+ path = '' if !path && !opaque # (see RFC2396 Section 5.2)
+ ret = [
+ scheme,
+ userinfo, host, port, # X
+ registry, # X
+ path, # Y
+ opaque, # Y
+ query,
+ fragment
+ ]
+ return ret
+ end
+
+=begin
+
+--- URI::parse(uri_str)
+
+=end
+ def self.parse(uri)
+ scheme, userinfo, host, port,
+ registry, path, opaque, query, fragment = self.split(uri)
+
+ if scheme && @@schemes.include?(scheme.upcase)
+ @@schemes[scheme.upcase].new(scheme, userinfo, host, port,
+ registry, path, opaque, query,
+ fragment)
+ else
+ Generic.new(scheme, userinfo, host, port,
+ registry, path, opaque, query,
+ fragment)
+ end
+ end
+
+=begin
+
+--- URI::extract(str[, schemes])
+
+=end
+ def self.extract(str, schemes = [])
+ urls = []
+ if schemes.size > 0
+ tmp = Regexp.new('(?:' + schemes.collect{|s|
+ Regexp.quote(s + ':')
+ }.join('|') + ')',
+ Regexp::IGNORECASE, 'N')
+ str.scan(tmp) {
+ tmp_str = $& + $'
+ if ABS_URI_REF =~ tmp_str
+ if block_given?
+ yield($&)
+ else
+ urls << $&
+ end
+ end
+ }
+
+ else
+ str.scan(ABS_URI_REF) {
+ if block_given?
+ yield($&)
+ else
+ urls << $&
+ end
+ }
+ end
+
+ if block_given?
+ return nil
+ else
+ return urls
+ end
+ end
+
+end # URI
diff --git a/lib/uri/ftp.rb b/lib/uri/ftp.rb
new file mode 100644
index 0000000000..391cd550e9
--- /dev/null
+++ b/lib/uri/ftp.rb
@@ -0,0 +1,149 @@
+#
+# $Id$
+#
+# Copyright (c) 2001 akira yamada <akira@ruby-lang.org>
+# You can redistribute it and/or modify it under the same term as Ruby.
+#
+
+require 'uri/generic'
+
+module URI
+
+=begin
+
+== URI::FTP
+
+=== Super Class
+
+((<URI::Generic>))
+
+=end
+
+ # RFC1738 section 3.2.
+ class FTP < Generic
+ DEFAULT_PORT = 21
+
+ COMPONENT = [
+ :scheme,
+ :userinfo, :host, :port,
+ :path, :typecode
+ ].freeze
+
+ TYPECODE = ['a', 'i', 'd'].freeze
+ TYPECODE_PREFIX = ';type='.freeze
+
+=begin
+
+=== Class Methods
+
+--- URI::FTP::build
+ Create a new URI::FTP object from components of URI::FTP with
+ check. It is scheme, userinfo, host, port, path and typecode. It
+ provided by an Array or a Hash. typecode is "a", "i" or "d".
+
+--- URI::FTP::new
+ Create a new URI::FTP object from ``generic'' components with no
+ check.
+
+=end
+
+ def self.new2(user, password, host, port, path,
+ typecode = nil, arg_check = true)
+ typecode = nil if typecode.size == 0
+ if typecode && !TYPECODE.include?(typecode)
+ raise ArgumentError,
+ "bad typecode is specified: #{typecode}"
+ end
+
+ # do escape
+
+ self.new('ftp',
+ [user, password],
+ host, port, nil,
+ typecode ? path + TYPECODE_PREFIX + typecode : path,
+ nil, nil, nil, arg_check)
+ end
+
+ def self.build(args)
+ tmp = Util::make_components_hash(self, args)
+
+ if tmp[:typecode]
+ if tmp[:typecode].size == 1
+ tmp[:typecode] = TYPECODE_PREFIX + tmp[:typecode]
+ end
+ tmp[:path] << tmp[:typecode]
+ end
+
+ return super(tmp)
+ end
+
+ def initialize(*arg)
+ super(*arg)
+ @typecode = nil
+ tmp = @path.index(TYPECODE_PREFIX)
+ if tmp
+ typecode = @path[tmp + TYPECODE_PREFIX.size..-1]
+ self.set_path(@path[0..tmp - 1])
+
+ if arg[-1]
+ self.typecode = typecode
+ else
+ self.set_typecode(typecode)
+ end
+ end
+ end
+ attr_reader :typecode
+
+ #
+ # methods for typecode
+ #
+
+ def check_typecode(v)
+ if TYPECODE.include?(v)
+ return true
+ else
+ raise InvalidComponentError,
+ "bad typecode(expected #{TYPECODE.join(', ')}): #{v}"
+ end
+ end
+ private :check_typecode
+
+ def set_typecode(v)
+ @typecode = v
+ end
+ protected :set_typecode
+
+ def typecode=(typecode)
+ check_typecode(typecode)
+ set_typecode(typecode)
+ end
+
+=begin
+=end
+ def merge(oth)
+ tmp = super(oth)
+ if self != tmp
+ tmp.set_typecode(oth.typecode)
+ end
+
+ return tmp
+ end
+
+=begin
+=end
+ def to_str
+ save_path = nil
+ if @typecode
+ save_path = @path
+ @path = @path + TYPECODE_PREFIX + @typecode
+ end
+ str = super
+ if @typecode
+ @path = save_path
+ end
+
+ return str
+ end
+ end # FTP
+ @@schemes['FTP'] = FTP
+end # URI
diff --git a/lib/uri/generic.rb b/lib/uri/generic.rb
new file mode 100644
index 0000000000..4d3ab67b7e
--- /dev/null
+++ b/lib/uri/generic.rb
@@ -0,0 +1,1087 @@
+#
+# $Id$
+#
+# Copyright (c) 2001 akira yamada <akira@ruby-lang.org>
+# You can redistribute it and/or modify it under the same term as Ruby.
+#
+
+require 'uri/common'
+
+module URI
+
+=begin
+
+== URI::Generic
+
+=== Super Class
+
+Object
+
+=end
+
+ class Generic
+ include REGEXP
+
+=begin
+
+=== Class Methods
+
+--- URI::Generic::default_port
+
+=end
+ DEFAULT_PORT = nil
+
+ def self.default_port
+ self::DEFAULT_PORT
+ end
+
+ def default_port
+ self.type.default_port
+ end
+
+=begin
+--- URI::Generic::component
+=end
+ COMPONENT = [
+ :scheme,
+ :userinfo, :host, :port, :registry,
+ :path, :opaque,
+ :query,
+ :fragment
+ ].freeze
+
+ def self.component
+ self::COMPONENT
+ end
+
+=begin
+
+--- URI::Generic::build2
+ At first, try to create a new URI::Generic object using
+ URI::Generic::build. But, if you get a exception
+ URI::InvalidComponentError, then re-try to create an object with
+ escaped components.
+
+--- URI::Generic::build
+ Create a new URI::Generic object from components of URI::Generic
+ with check. It is scheme, userinfo, host, port, registry, path,
+ opaque, query and fragment. It provided by an Array of a Hash.
+
+--- URI::Generic::new
+ Create new URI::Generic object from ``generic'' components with no
+ check.
+
+=end
+ def self.build2(args)
+ begin
+ return self.build(args)
+ rescue InvalidComponentError
+ if args.kind_of?(Array)
+ return self.build(args.collect{|x|
+ if x
+ URI.escape(x)
+ else
+ x
+ end
+ })
+ elsif args.kind_of?(Hash)
+ tmp = {}
+ args.each do |key, value|
+ tmp[key] = if value
+ URI.escape(value)
+ else
+ value
+ end
+ end
+ return self.build(tmp)
+ end
+ end
+ end
+
+ def self.build(args)
+ if args.kind_of?(Array) &&
+ args.size == ::URI::Generic::COMPONENT.size
+ tmp = args
+ elsif args.kind_of?(Hash)
+ tmp = ::URI::Generic::COMPONENT.collect do |c|
+ if args.include?(c)
+ args[c]
+ else
+ nil
+ end
+ end
+ else
+ raise ArgumentError,
+ "expected Array of or Hash of compornents of #{self.type} (#{self.type.component.join(', ')})"
+ end
+
+ tmp << true
+ return self.new(*tmp)
+ end
+
+ def initialize(scheme,
+ userinfo, host, port, registry,
+ path, opaque,
+ query,
+ fragment,
+ arg_check = false)
+ @scheme = nil
+ @user = nil
+ @password = nil
+ @host = nil
+ @port = nil
+ @path = nil
+ @query = nil
+ @opaque = nil
+ @registry = nil
+ @fragment = nil
+
+ if arg_check
+ self.scheme = scheme
+ self.userinfo = userinfo
+ self.host = host
+ self.port = port
+ self.path = path
+ self.query = query
+ self.opaque = opaque
+ self.registry = registry
+ self.fragment = fragment
+ else
+ self.set_scheme(scheme)
+ self.set_userinfo(userinfo)
+ self.set_host(host)
+ self.set_port(port)
+ self.set_path(path)
+ self.set_query(query)
+ self.set_opaque(opaque)
+ self.set_registry(registry)
+ self.set_fragment(fragment)
+ end
+
+ @scheme.freeze if @scheme
+ self.set_path('') if !@path && !@opaque # (see RFC2396 Section 5.2)
+ self.set_port(self.default_port) if self.default_port && !@port
+ end
+ attr_reader :scheme
+ attr_reader :host
+ attr_reader :port
+ attr_reader :registry
+ attr_reader :path
+ attr_reader :query
+ attr_reader :opaque
+ attr_reader :fragment
+
+=begin
+
+=== Instance Methods
+
+=end
+
+=begin
+
+--- URI::Generic#component
+
+=end
+ def component
+ self.type.component
+ end
+
+ # set_XXX method sets value to @XXX instance variable with no check,
+ # so be careful if you use these methods. or, you use these method
+ # with check_XXX method, or you use XXX= methods.
+
+=begin
+
+--- URI::Generic#scheme
+
+--- URI::Generic#scheme=(v)
+
+=end
+ #
+ # methods for scheme
+ #
+ def check_scheme(v)
+ if v && SCHEME !~ v
+ raise InvalidComponentError,
+ "bad component(expected scheme component): #{v}"
+ end
+
+ return true
+ end
+ private :check_scheme
+
+ def set_scheme(v)
+ @scheme = v
+ end
+ protected :set_scheme
+
+ def scheme=(v)
+ check_scheme(v)
+ set_scheme(v)
+ end
+
+=begin
+
+--- URI::Generic#userinfo
+
+--- URI::Generic#userinfo=(v)
+
+--- URI::Generic#user
+
+--- URI::Generic#user=(v)
+
+--- URI::Generic#password
+
+--- URI::Generic#password=(v)
+
+=end
+ #
+ # methods for userinfo
+ #
+ def check_userinfo(user, password = nil)
+ if (user || password) &&
+ (@registry || @opaque)
+ raise InvalidURIError,
+ "can not set userinfo with registry or opaque"
+ end
+
+ if !password
+ user, password = split_userinfo(user)
+ end
+ check_user(user)
+ check_password(password)
+
+ return true
+ end
+ private :check_userinfo
+
+ def check_user(v)
+ return v unless v
+
+ if USERINFO !~ v
+ raise InvalidComponentError,
+ "bad component(expected userinfo component or user component): #{v}"
+ end
+
+ return true
+ end
+ private :check_user
+
+ def check_password(v)
+ return v unless v
+
+ if !@password
+ raise InvalidURIError,
+ "password component depends user component"
+ end
+
+ if USERINFO !~ v
+ raise InvalidComponentError,
+ "bad component(expected user component): #{v}"
+ end
+
+ return true
+ end
+ private :check_password
+
+ def userinfo=(user, password = nil)
+ check_userinfo(user, password)
+ set_userinfo(user, password)
+ end
+
+ def user=(user)
+ check_user(user)
+ set_user(user)
+ end
+
+ def password=(password)
+ check_password(password)
+ set_password(password)
+ end
+
+ def set_userinfo(user, password = nil)
+ if !password
+ user, password = split_userinfo(user)
+ end
+ @user = user
+ @password = password
+ end
+ protected :set_userinfo
+
+ def set_user(v)
+ set_userinfo(v, @password)
+ end
+ protected :set_user
+
+ def set_password(v)
+ set_userinfo(@user, v)
+ end
+ protected :set_password
+
+ def split_userinfo(ui)
+ return nil, nil unless ui
+ tmp = ui.index(':')
+ if tmp
+ user = ui[0..tmp - 1]
+ password = ui[tmp + 1..-1]
+ else
+ user = ui
+ password = nil
+ end
+
+ return user, password
+ end
+ private :split_userinfo
+
+ def escape_userpass(v)
+ v = URI.escape(v, /[@:\/]/o) # RFC 1738 section 3.1 #/
+ end
+ private :escape_userpass
+
+ def userinfo
+ if !@password
+ @user
+ else
+ @user + ':' + @password
+ end
+ end
+
+ def user
+ @user
+ end
+
+ def password
+ @password
+ end
+
+=begin
+
+--- URI::Generic#host
+
+--- URI::Generic#host=(v)
+
+=end
+ #
+ # methods for host
+ #
+
+ def check_host(v)
+ return v unless v
+
+ if @registry || @opaque
+ raise InvalidURIError,
+ "can not set host with registry or opaque"
+ elsif HOST !~ v
+ raise InvalidComponentError,
+ "bad component(expected host component): #{v}"
+ end
+
+ return true
+ end
+ private :check_host
+
+ def set_host(v)
+ @host = v
+ end
+ protected :set_host
+
+ def host=(v)
+ check_host(v)
+ set_host(v)
+ end
+
+=begin
+
+--- URI::Generic#port
+
+--- URI::Generic#port=(v)
+
+=end
+ #
+ # methods for port
+ #
+
+ def check_port(v)
+ return v unless v
+
+ if @registry || @opaque
+ raise InvalidURIError,
+ "can not set port with registry or opaque"
+ elsif !v.kind_of?(Fixnum) && PORT !~ v
+ raise InvalidComponentError,
+ "bad component(expected port component): #{v}"
+ end
+
+ return true
+ end
+ private :check_port
+
+ def set_port(v)
+ v = v.to_i if v && !v.kind_of?(Fixnum)
+ @port = v
+ end
+ protected :set_port
+
+ def port=(v)
+ check_port(v)
+ set_port(v)
+ end
+
+=begin
+
+--- URI::Generic#registry
+
+--- URI::Generic#registry=(v)
+
+=end
+ #
+ # methods for registry
+ #
+
+ def check_registry(v)
+ return v unless v
+
+ # raise if both server and registry are not nil, because:
+ # authority = server | reg_name
+ # server = [ [ userinfo "@" ] hostport ]
+ if @host || @port || @user # userinfo = @user + ':' + @password
+ raise InvalidURIError,
+ "can not set registry with host, port, or userinfo"
+ elsif v && REGISTRY !~ v
+ raise InvalidComponentError,
+ "bad component(expected registry component): #{v}"
+ end
+
+ return true
+ end
+ private :check_registry
+
+ def set_registry(v)
+ @registry = v
+ end
+ protected :set_registry
+
+ def registry=(v)
+ check_registry(v)
+ set_registry(v)
+ end
+
+=begin
+
+--- URI::Generic#path
+
+--- URI::Generic#path=(v)
+
+=end
+ #
+ # methods for path
+ #
+
+ def check_path(v)
+ # raise if both hier and opaque are not nil, because:
+ # absoluteURI = scheme ":" ( hier_part | opaque_part )
+ # hier_part = ( net_path | abs_path ) [ "?" query ]
+ if v && @opaque
+ raise InvalidURIError,
+ "path conflicts with opaque"
+ end
+
+ if @scheme
+ if v && v != '' && ABS_PATH !~ v
+ raise InvalidComponentError,
+ "bad component(expected absolute path component): #{v}"
+ end
+ else
+ if v && v != '' && ABS_PATH !~ v && REL_PATH !~ v
+ raise InvalidComponentError,
+ "bad component(expected relative path component): #{@path}"
+ end
+ end
+
+ return true
+ end
+ private :check_path
+
+ def set_path(v)
+ @path = v
+ end
+ protected :set_path
+
+ def path=(v)
+ check_path(v)
+ set_path(v)
+ end
+
+=begin
+
+--- URI::Generic#query
+
+--- URI::Generic#query=(v)
+
+=end
+ #
+ # methods for query
+ #
+
+ def check_query(v)
+ return v unless v
+
+ # raise if both hier and opaque are not nil, because:
+ # absoluteURI = scheme ":" ( hier_part | opaque_part )
+ # hier_part = ( net_path | abs_path ) [ "?" query ]
+ if @opaque
+ raise InvalidURIError,
+ "query conflicts with opaque"
+ end
+
+ if v && v != '' && QUERY !~ v
+ raise InvalidComponentError,
+ "bad component(expected query component): #{v}"
+ end
+
+ return true
+ end
+ private :check_query
+
+ def set_query(v)
+ @query = v
+ end
+ protected :set_query
+
+ def query=(v)
+ check_query(v)
+ set_query(v)
+ end
+
+=begin
+
+--- URI::Generic#opaque
+
+--- URI::Generic#opaque=(v)
+
+=end
+ #
+ # methods for opaque
+ #
+
+ def check_opaque(v)
+ return v unless v
+
+ # raise if both hier and opaque are not nil, because:
+ # absoluteURI = scheme ":" ( hier_part | opaque_part )
+ # hier_part = ( net_path | abs_path ) [ "?" query ]
+ if @host || @port || @usr || @path # userinfo = @user + ':' + @password
+ raise InvalidURIError,
+ "can not set opaque with host, port, userinfo or path"
+ elsif v && OPAQUE !~ v
+ raise InvalidComponentError,
+ "bad component(expected opaque component): #{v}"
+ end
+
+ return true
+ end
+ private :check_opaque
+
+ def set_opaque(v)
+ @opaque = v
+ end
+ protected :set_opaque
+
+ def opaque=(v)
+ check_opaque(v)
+ set_opaque(v)
+ end
+
+=begin
+
+--- URI::Generic#fragment
+
+--- URI::Generic#fragment=(v)
+
+=end
+ #
+ # methods for fragment
+ #
+
+ def check_fragment(v)
+ return v unless v
+
+ if v && v != '' && FRAGMENT !~ v
+ raise InvalidComponentError,
+ "bad component(expected fragment component): #{v}"
+ end
+
+ return true
+ end
+ private :check_fragment
+
+ def set_fragment(v)
+ @fragment = v
+ end
+ protected :set_fragment
+
+ def fragment=(v)
+ check_fragment(v)
+ set_fragment(v)
+ end
+
+=begin
+
+--- URI::Generic#hierarchical?
+
+=end
+ def hierarchical?
+ if @path
+ true
+ else
+ false
+ end
+ end
+
+=begin
+
+--- URI::Generic#absolute?
+
+=end
+ def absolute?
+ if @scheme
+ true
+ else
+ false
+ end
+ end
+ alias absolute absolute?
+
+=begin
+
+--- URI::Generic#relative?
+
+=end
+ def relative?
+ !absolute?
+ end
+
+=begin
+
+--- URI::Generic#merge(rel)
+--- URI::Generic#+(rel)
+
+=end
+ def split_path(path)
+ path.split(%r{/+}, -1)
+ end
+ private :split_path
+
+ def merge_path(base, rel)
+ # RFC2396, Section 5.2, 5)
+ if rel[0] == ?/ #/
+ # RFC2396, Section 5.2, 5)
+ return rel
+
+ else
+ # RFC2396, Section 5.2, 6)
+ base_path = split_path(base)
+ rel_path = split_path(rel)
+
+ if base_path.empty?
+ base_path = [''] # XXX
+ end
+
+ # RFC2396, Section 5.2, 6), a)
+ base_path.pop if !base_path.last.empty?
+
+ # RFC2396, Section 5.2, 6), c)
+ # RFC2396, Section 5.2, 6), d)
+ rel_path.push('') if rel_path.last == '.'
+ rel_path.delete('.')
+
+ # RFC2396, Section 5.2, 6), e)
+ tmp = []
+ rel_path.each do |x|
+ if x == '..' &&
+ !(tmp.empty? || tmp.last == '..')
+ tmp.pop
+ else
+ tmp << x
+ end
+ end
+
+ add_trailer_slash = true
+ while x = tmp.shift
+ if x == '..' && base_path.size > 1
+ # RFC2396, Section 4
+ # a .. or . in an absolute path has no special meaning
+ base_path.pop
+ else
+ # if x == '..'
+ # valid absolute (but abnormal) path "/../..."
+ # else
+ # valid absolute path
+ # end
+ base_path << x
+ base_path += tmp
+ add_trailer_slash = false
+ break
+ end
+ end
+ base_path.push('') if add_trailer_slash
+
+ return base_path.join('/')
+ end
+ end
+ private :merge_path
+
+ # abs(self) + rel(oth) => abs(new)
+ def merge(oth)
+ base, rel = merge0(oth)
+ if base == rel
+ return base
+ end
+
+ authority = rel.userinfo || rel.host || rel.port
+
+ # RFC2396, Section 5.2, 2)
+ if rel.path.empty? && !authority && !rel.query
+ base.set_fragment(rel.fragment) if rel.fragment
+ return base
+ end
+
+ base.set_query(nil)
+ base.set_fragment(nil)
+
+ # RFC2396, Section 5.2, 4)
+ if !authority
+ base.set_path(merge_path(base.path, rel.path))
+ else
+ # RFC2396, Section 5.2, 4)
+ base.set_path(rel.path)
+ end
+
+ # RFC2396, Section 5.2, 7)
+ base.set_userinfo(rel.userinfo) if rel.userinfo
+ base.set_host(rel.host) if rel.host
+ base.set_port(rel.port) if rel.port
+ base.set_query(rel.query) if rel.query
+ base.set_fragment(rel.fragment) if rel.fragment
+
+ return base
+ end # merge
+ alias + merge
+
+ # return base and rel.
+ # you can modify `base', but can not `rel'.
+ def merge0(oth)
+ case oth
+ when Generic
+ when String
+ oth = URI.parse(oth)
+ else
+ raise ArgumentError,
+ "bad argument(expected URI object or URI string)"
+ end
+
+ if self.relative? && oth.relative?
+ raise BadURIError,
+ "both URI are relative"
+ end
+
+ if self.absolute? && oth.absolute?
+ #raise BadURIError,
+ # "both URI are absolute"
+ # hmm... should return oth for usability?
+ return oth, oth
+ end
+
+ if !self.hierarchical?
+ raise BadURIError,
+ "not hierarchical URI: #{self}"
+ elsif !oth.hierarchical?
+ raise BadURIError,
+ "not hierarchical URI: #{oth}"
+ end
+
+ if self.absolute?
+ return self.dup, oth
+ else
+ return oth.dup, self
+ end
+ end
+ private :merge0
+
+=begin
+
+--- URI::Generic#route_from(src)
+--- URI::Generic#-(src)
+
+=end
+ def route_from_path(src, dst)
+ # RFC2396, Section 4.2
+ return '' if src == dst
+
+ src_path = split_path(src)
+ dst_path = split_path(dst)
+
+ # hmm... dst has abnormal absolute path,
+ # like "/./", "/../", "/x/../", ...
+ if dst_path.include?('..') ||
+ dst_path.include?('.')
+ return dst.dup
+ end
+
+ src_path.pop
+
+ # discard same parts
+ while dst_path.first == src_path.first
+ break if dst_path.empty?
+
+ src_path.shift
+ dst_path.shift
+ end
+
+ tmp = dst_path.join('/')
+
+ # calculate
+ if src_path.empty?
+ if tmp.empty?
+ return './'
+ elsif dst_path.first.include?(':') # (see RFC2396 Section 5)
+ return './' + tmp
+ else
+ return tmp
+ end
+ end
+
+ return '../' * src_path.size + tmp
+ end
+ private :route_from_path
+
+ def route_from0(oth)
+ case oth
+ when Generic
+ when String
+ oth = URI.parse(oth)
+ else
+ raise ArgumentError,
+ "bad argument(expected URI object or URI string)"
+ end
+
+ if self.relative?
+ raise BadURIError,
+ "relative URI: #{self}"
+ end
+ if oth.relative?
+ raise BadURIError,
+ "relative URI: #{oth}"
+ end
+
+ if !self.hierarchical? || !oth.hierarchical?
+ return self, self.dup
+ end
+
+ if self.scheme != oth.scheme
+ return oth, oth.dup
+ end
+ rel = URI::Generic.new(nil, # it is relative URI
+ self.userinfo, self.host, self.port,
+ self.registry, self.path, self.opaque,
+ self.query, self.fragment)
+
+ if rel.userinfo != oth.userinfo ||
+ rel.host != oth.host ||
+ rel.port != oth.port
+ rel.set_port(nil) if rel.port == oth.default_port
+ return rel, rel
+ end
+ rel.set_userinfo(nil)
+ rel.set_host(nil)
+ rel.set_port(nil)
+
+ if rel.path == oth.path
+ rel.set_path('')
+ rel.set_query(nil) if rel.query == oth.query
+ return rel, rel
+ end
+
+ # you can modify `rel', but can not `oth'.
+ return oth, rel
+ end
+ private :route_from0
+
+ # calculate relative path from oth to self
+ def route_from(oth)
+ # you can modify `rel', but can not `oth'.
+ oth, rel = route_from0(oth)
+ if oth == rel
+ return rel
+ end
+
+ rel.set_path(route_from_path(oth.path, self.path))
+ if rel.path == './' && self.query
+ # "./?foo" -> "?foo"
+ rel.set_path('')
+ end
+
+ return rel
+ end
+ # abs1 - abs2 => relative_path_to_abs1_from_abs2
+ # (see http://www.nikonet.or.jp/spring/what_v/what_v_4.htm :-)
+ alias - route_from
+
+=begin
+
+--- URI::Generic#route_to(dst)
+
+=end
+ # calculate relative path to oth from self
+ def route_to(oth)
+ case oth
+ when Generic
+ when String
+ oth = URI.parse(oth)
+ else
+ raise ArgumentError,
+ "bad argument(expected URI object or URI string)"
+ end
+
+ oth.route_from(self)
+ end
+
+=begin
+
+--- URI::Generic#normalize
+--- URI::Generic#normalize!
+
+=end
+ def normalize
+ uri = dup
+ uri.normalize!
+ uri
+ end
+
+ def normalize!
+ if path && path == ''
+ set_path('/')
+ end
+ if host && host != host.downcase
+ set_host(self.host.downcase)
+ end
+ end
+
+=begin
+
+--- URI::Generic#to_s
+
+=end
+ def path_query
+ str = @path
+ if @query
+ str += '?' + @query
+ end
+ str
+ end
+ private :path_query
+
+ def to_str
+ str = ''
+ if @scheme
+ str << @scheme
+ str << ':'
+ end
+
+ if @opaque
+ str << @opaque
+
+ else
+ if @registry
+ str << @registry
+ else
+ if @host
+ str << '//'
+ end
+ if self.userinfo
+ str << self.userinfo
+ str << '@'
+ end
+ if @host
+ str << @host
+ end
+ if @port && @port != self.default_port
+ str << ':'
+ str << @port.to_s
+ end
+ end
+
+ str << path_query
+ end
+
+ if @fragment
+ str << '#'
+ str << @fragment
+ end
+
+ str
+ end
+
+ def to_s
+ to_str
+ end
+
+=begin
+
+--- URI::Generic#==(oth)
+
+=end
+ def ==(oth)
+ if oth.kind_of?(String)
+ oth = URI.parse(oth)
+ end
+
+ if self.class == oth.class
+ self.normalize.to_ary == oth.normalize.to_ary
+ else
+ false
+ end
+ end
+
+=begin
+
+--- URI::Generic#===(oth)
+
+=end
+# def ===(oth)
+# raise NotImplementedError
+# end
+
+=begin
+--- URI::Generic#to_a
+=end
+ def to_ary
+ component.collect do |x|
+ self.send(x)
+ end
+ end
+
+ def to_a
+ to_ary
+ end
+
+=begin
+=end
+ def inspect
+ sprintf("#<%s:0x%x URL:%s>", self.type.to_s, self.id, self.to_s)
+ end
+
+=begin
+=end
+ def coerce(oth)
+ case oth
+ when String
+ oth = URI.parse(oth)
+ else
+ super
+ end
+
+ return oth, self
+ end
+ end # Generic
+end # URI
diff --git a/lib/uri/http.rb b/lib/uri/http.rb
new file mode 100644
index 0000000000..3e5b209d49
--- /dev/null
+++ b/lib/uri/http.rb
@@ -0,0 +1,76 @@
+#
+# $Id$
+#
+# Copyright (c) 2001 akira yamada <akira@ruby-lang.org>
+# You can redistribute it and/or modify it under the same term as Ruby.
+#
+
+require 'uri/generic'
+
+module URI
+
+=begin
+
+== URI::HTTP
+
+=== Super Class
+
+((<URI::Generic>))
+
+=end
+
+ # RFC1738 section 3.3.
+ class HTTP < Generic
+ DEFAULT_PORT = 80
+
+ COMPONENT = [
+ :scheme,
+ :userinfo, :host, :port,
+ :path,
+ :query,
+ :fragment
+ ].freeze
+
+=begin
+
+=== Class Methods
+
+--- URI::HTTP::build
+ Create a new URI::HTTP object from components of URI::HTTP with
+ check. It is scheme, userinfo, host, port, path, query and
+ fragment. It provided by an Array of a Hash.
+
+--- URI::HTTP::new
+ Create a new URI::HTTP object from ``generic'' components with no
+ check.
+
+=end
+
+ def self.build(args)
+ tmp = Util::make_components_hash(self, args)
+ return super(tmp)
+ end
+
+ def initialize(*arg)
+ super(*arg)
+ end
+
+=begin
+
+=== Instance Methods
+
+--- URI::HTTP#request_uri
+
+=end
+ def request_uri
+ r = path_query
+ if r[0] != ?/
+ r = '/' + r
+ end
+
+ r
+ end
+ end # HTTP
+
+ @@schemes['HTTP'] = HTTP
+end # URI
diff --git a/lib/uri/https.rb b/lib/uri/https.rb
new file mode 100644
index 0000000000..691198fafa
--- /dev/null
+++ b/lib/uri/https.rb
@@ -0,0 +1,26 @@
+#
+# $Id$
+#
+# Copyright (c) 2001 akira yamada <akira@ruby-lang.org>
+# You can redistribute it and/or modify it under the same term as Ruby.
+#
+
+require 'uri/http'
+
+module URI
+
+=begin
+
+== URI::HTTPS
+
+=== Super Class
+
+((<URI::HTTP>))
+
+=end
+
+ class HTTPS < HTTP
+ DEFAULT_PORT = 443
+ end
+ @@schemes['HTTPS'] = HTTPS
+end # URI
diff --git a/lib/uri/ldap.rb b/lib/uri/ldap.rb
new file mode 100644
index 0000000000..441ee69b86
--- /dev/null
+++ b/lib/uri/ldap.rb
@@ -0,0 +1,238 @@
+#
+# $Id$
+#
+
+require 'uri/generic'
+
+module URI
+
+=begin
+
+== URI::LDAP
+
+URI::LDAP is copyrighted free software by Takaaki Tateishi and akira yamada.
+
+ Copyright (c) 2001 Takaaki Tateishi <ttate@jaist.ac.jp> and
+ akira yamada <akira@ruby-lang.org>.
+ You can redistribute it and/or modify it under the same term as Ruby.
+
+=== Super Class
+
+((<URI::Generic>))
+
+=end
+
+ # LDAP URI SCHEMA (described in RFC2255)
+ # ldap://<host>/<dn>[?<attrs>[?<scope>[?<filter>[?<extensions>]]]]
+ class LDAP < Generic
+
+ DEFAULT_PORT = 389
+
+ COMPONENT = [
+ :scheme,
+ :host, :port,
+ :dn,
+ :attributes,
+ :scope,
+ :filter,
+ :extensions,
+ ].freeze
+
+ SCOPE = [
+ SCOPE_ONE = 'one',
+ SCOPE_SUB = 'sub',
+ SCOPE_BASE = 'base',
+ ].freeze
+
+=begin
+
+=== Class Methods
+
+--- URI::LDAP::build
+
+--- URI::LDAP::new
+
+=end
+
+ def self.build(args)
+ tmp = Util::make_components_hash(self, args)
+
+ if tmp[:dn]
+ tmp[:path] = tmp[:dn]
+ end
+
+ query = []
+ [:extensions, :filter, :scope, :attributes].collect do |x|
+ next if !tmp[x] && query.size == 0
+ query.unshift(tmp[x])
+ end
+
+ tmp[:query] = query.join('?')
+
+ return super(tmp)
+ end
+
+ def initialize(*arg)
+ super(*arg)
+
+ if @fragment
+ raise InvalidURIError, 'bad LDAP URL'
+ end
+
+ parse_dn
+ parse_query
+ end
+
+ def parse_dn
+ @dn = @path[1..-1]
+ end
+ private :parse_dn
+
+ def parse_query
+ @attributes = nil
+ @scope = nil
+ @filter = nil
+ @extensions = nil
+
+ if @query
+ attrs, scope, filter, extensions = @query.split('?')
+
+ @attributes = attrs if attrs && attrs.size > 0
+ @scope = scope if scope && scope.size > 0
+ @filter = filter if filter && filter.size > 0
+ @extensions = extensions if extensions && extensions.size > 0
+ end
+ end
+ private :parse_query
+
+ def build_path_query
+ @path = '/' + @dn
+
+ query = []
+ [@extensions, @filter, @scope, @attributes].each do |x|
+ next if !x && query.size == 0
+ query.unshift(x)
+ end
+ @query = query.join('?')
+ end
+ private :build_path_query
+
+=begin
+
+=== Instance Methods
+
+--- URI::LDAP#dn
+
+--- URI::LDAP#dn=(v)
+
+=end
+
+ def dn
+ @dn
+ end
+
+ def set_dn(val)
+ @dn = val
+ build_path_query
+ end
+ protected :set_dn
+
+ def dn=(val)
+ set_dn(val)
+ end
+
+=begin
+
+--- URI::LDAP#attributes
+
+--- URI::LDAP#attributes=(v)
+
+=end
+
+ def attributes
+ @attributes
+ end
+
+ def set_attributes(val)
+ @attributes = val
+ build_path_query
+ end
+ protected :set_attributes
+
+ def attributes=(val)
+ set_attributes(val)
+ end
+
+=begin
+
+--- URI::LDAP#scope
+
+--- URI::LDAP#scope=(v)
+
+=end
+
+ def scope
+ @scope
+ end
+
+ def set_scope(val)
+ @scope = val
+ build_path_query
+ end
+ protected :set_scope
+
+ def scope=(val)
+ set_scope(val)
+ end
+
+=begin
+
+--- URI::LDAP#filter
+
+--- URI::LDAP#filter=(v)
+
+=end
+
+ def filter
+ @filter
+ end
+
+ def set_filter(val)
+ @filter = val
+ build_path_query
+ end
+ protected :set_filter
+
+ def filter=(val)
+ set_filter(val)
+ end
+
+=begin
+
+--- URI::LDAP#extensions
+
+--- URI::LDAP#extensions=(v)
+
+=end
+
+ def extensions
+ @extensions
+ end
+
+ def set_extensions(val)
+ @extensions = val
+ build_path_query
+ end
+ protected :set_extensions
+
+ def extensions=(val)
+ set_extensions(val)
+ end
+ end
+
+ def hierarchical?
+ false
+ end
+
+ @@schemes['LDAP'] = LDAP
+end
diff --git a/lib/uri/mailto.rb b/lib/uri/mailto.rb
new file mode 100644
index 0000000000..bf6b954903
--- /dev/null
+++ b/lib/uri/mailto.rb
@@ -0,0 +1,260 @@
+#
+# $Id$
+#
+# Copyright (c) 2001 akira yamada <akira@ruby-lang.org>
+# You can redistribute it and/or modify it under the same term as Ruby.
+#
+
+require 'uri/generic'
+
+module URI
+
+=begin
+
+== URI::MailTo
+
+=== Super Class
+
+((<URI::Generic>))
+
+=end
+
+ # RFC2368, The mailto URL scheme
+ class MailTo < Generic
+ include REGEXP
+
+ DEFAULT_PORT = nil
+
+ COMPONENT = [
+ :scheme,
+ :to, :headers
+ ].freeze
+
+ # "hname" and "hvalue" are encodings of an RFC 822 header name and
+ # value, respectively. As with "to", all URL reserved characters must
+ # be encoded.
+ #
+ # "#mailbox" is as specified in RFC 822 [RFC822]. This means that it
+ # consists of zero or more comma-separated mail addresses, possibly
+ # including "phrase" and "comment" components. Note that all URL
+ # reserved characters in "to" must be encoded: in particular,
+ # parentheses, commas, and the percent sign ("%"), which commonly occur
+ # in the "mailbox" syntax.
+ #
+ # Within mailto URLs, the characters "?", "=", "&" are reserved.
+
+ # hname = *urlc
+ # hvalue = *urlc
+ # header = hname "=" hvalue
+ HEADER_REGEXP = "(?:[^?=&]*=[^?=&]*)".freeze
+ # headers = "?" header *( "&" header )
+ # to = #mailbox
+ # mailtoURL = "mailto:" [ to ] [ headers ]
+ MAILBOX_REGEXP = "(?:[^(),%?=&]|#{PATTERN::ESCAPED})".freeze
+ MAILTO_REGEXP = Regexp.new("
+ \\A
+ (#{MAILBOX_REGEXP}*?) (?# 1: to)
+ (?:
+ \\?
+ (#{HEADER_REGEXP}(?:\\&#{HEADER_REGEXP})*) (?# 2: headers)
+ )?
+ \\z
+ ", Regexp::EXTENDED, 'N').freeze
+
+=begin
+
+=== Class Methods
+
+--- URI::MailTo::build
+ Create a new URI::MailTo object from components of URI::MailTo
+ with check. It is to and headers. It provided by an Array of a
+ Hash. You can provide headers as an String like
+ "subject=subscribe&cc=addr" or an Array like [["subject",
+ "subscribe"], ["cc", "addr"]]
+
+--- URI::MailTo::new
+ Create a new URI::MailTo object from ``generic'' components with
+ no check. Because, this method is usually called from URI::parse
+ and the method checks validity of each components.
+
+=end
+
+ def self.build(args)
+ tmp = Util::make_components_hash(self, args)
+
+ if tmp[:to]
+ tmp[:opaque] = tmp[:to]
+ 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].to_s
+ 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
+ end
+ end
+
+ return super(tmp)
+ end
+
+ def initialize(*arg)
+ super(*arg)
+
+ @to = nil
+ @headers = []
+
+ if MAILTO_REGEXP =~ @opaque
+ if arg[-1]
+ self.to = $1
+ self.headers = $2
+ else
+ set_to($1)
+ set_headers($2)
+ end
+ elsif arg[-1]
+ raise InvalidComponentError,
+ "unrecognised opaque part for mailtoURL: #{@opaque}"
+ end
+ end
+ attr_reader :to
+ attr_reader :headers
+
+=begin
+
+=== Instance Methods
+
+--- URI::MailTo#to
+
+--- URI::MailTo#to=(v)
+
+=end
+
+ #
+ # methods for to
+ #
+
+ def check_to(v)
+ return true unless v
+ return true if v.size == 0
+
+ if OPAQUE !~ v || /\A#{MAILBOX_REGEXP}*\z/o !~ v
+ raise InvalidComponentError,
+ "bad component(expected opaque component): #{v}"
+ end
+
+ return true
+ end
+ private :check_to
+
+ def set_to(v)
+ @to = v
+ end
+ protected :set_to
+
+ def to=(v)
+ check_to(v)
+ set_to(v)
+ end
+
+=begin
+
+--- URI::MailTo#headers
+
+--- URI::MailTo#headers=(v)
+
+=end
+
+ #
+ # methods for headers
+ #
+
+ def check_headers(v)
+ return true unless v
+ return true if v.size == 0
+
+ if OPAQUE !~ v ||
+ /\A(#{HEADER_REGEXP}(?:\&#{HEADER_REGEXP})*)\z/o !~ v
+ raise InvalidComponentError,
+ "bad component(expected opaque component): #{v}"
+ end
+
+ return true
+ end
+ private :check_headers
+
+ def set_headers(v)
+ @headers = []
+ if v
+ v.scan(HEADER_REGEXP) do |x|
+ @headers << x.split(/=/o, 2)
+ end
+ end
+ end
+ protected :set_headers
+
+ def headers=(v)
+ check_headers(v)
+ set_headers(v)
+ end
+
+ def to_str
+ @scheme + ':' +
+ if @to
+ @to
+ else
+ ''
+ end +
+ if @headers.size > 0
+ '?' + @headers.collect{|x| x.join('=')}.join('&')
+ else
+ ''
+ end
+ end
+
+=begin
+
+--- URI::MailTo#to_mailtext
+
+=end
+ def to_mailtext
+ to = URI::unescape(@to)
+ head = ''
+ body = ''
+ @headers.each do |x|
+ case x[0]
+ when 'body'
+ body = URI::unescape(x[1])
+ when 'to'
+ to << ', ' + URI::unescape(x[1])
+ else
+ head << URI::unescape(x[0]).capitalize + ': ' +
+ URI::unescape(x[1]) + "\n"
+ end
+ end
+
+ return "To: #{to}
+#{head}
+#{body}
+"
+ end
+ alias to_rfc822text to_mailtext
+ end # MailTo
+
+ @@schemes['MAILTO'] = MailTo
+end # URI