1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
|
require 'openid/util'
require 'openid/fetchers'
require 'openid/yadis/constants'
require 'openid/yadis/parsehtml'
module OpenID
# Raised when a error occurs in the discovery process
class DiscoveryFailure < OpenIDError
attr_accessor :identity_url, :http_response
def initialize(message, http_response)
super(message)
@identity_url = nil
@http_response = http_response
end
end
module Yadis
# Contains the result of performing Yadis discovery on a URI
class DiscoveryResult
# The result of following redirects from the request_uri
attr_accessor :normalize_uri
# The URI from which the response text was returned (set to
# nil if there was no XRDS document found)
attr_accessor :xrds_uri
# The content-type returned with the response_text
attr_accessor :content_type
# The document returned from the xrds_uri
attr_accessor :response_text
attr_accessor :request_uri, :normalized_uri
def initialize(request_uri)
# Initialize the state of the object
#
# sets all attributes to None except the request_uri
@request_uri = request_uri
@normalized_uri = nil
@xrds_uri = nil
@content_type = nil
@response_text = nil
end
# Was the Yadis protocol's indirection used?
def used_yadis_location?
return @normalized_uri != @xrds_uri
end
# Is the response text supposed to be an XRDS document?
def is_xrds
return (used_yadis_location?() or
@content_type == YADIS_CONTENT_TYPE)
end
end
# Discover services for a given URI.
#
# uri: The identity URI as a well-formed http or https URI. The
# well-formedness and the protocol are not checked, but the
# results of this function are undefined if those properties do
# not hold.
#
# returns a DiscoveryResult object
#
# Raises DiscoveryFailure when the HTTP response does not have
# a 200 code.
def self.discover(uri)
result = DiscoveryResult.new(uri)
begin
resp = OpenID.fetch(uri, nil, {'Accept' => YADIS_ACCEPT_HEADER})
rescue Exception
raise DiscoveryFailure.new("Failed to fetch identity URL #{uri} : #{$!}", $!)
end
if resp.code != "200" and resp.code != "206"
raise DiscoveryFailure.new(
"HTTP Response status from identity URL host is not \"200\"."\
"Got status #{resp.code.inspect} for #{resp.final_url}", resp)
end
# Note the URL after following redirects
result.normalized_uri = resp.final_url
# Attempt to find out where to go to discover the document or if
# we already have it
result.content_type = resp['content-type']
result.xrds_uri = self.where_is_yadis?(resp)
if result.xrds_uri and result.used_yadis_location?
begin
resp = OpenID.fetch(result.xrds_uri)
rescue
raise DiscoveryFailure.new("Failed to fetch Yadis URL #{result.xrds_uri} : #{$!}", $!)
end
if resp.code != "200" and resp.code != "206"
exc = DiscoveryFailure.new(
"HTTP Response status from Yadis host is not \"200\". " +
"Got status #{resp.code.inspect} for #{resp.final_url}", resp)
exc.identity_url = result.normalized_uri
raise exc
end
result.content_type = resp['content-type']
end
result.response_text = resp.body
return result
end
# Given a HTTPResponse, return the location of the Yadis
# document.
#
# May be the URL just retrieved, another URL, or None, if I
# can't find any.
#
# [non-blocking]
def self.where_is_yadis?(resp)
# Attempt to find out where to go to discover the document or if
# we already have it
content_type = resp['content-type']
# According to the spec, the content-type header must be an
# exact match, or else we have to look for an indirection.
if (!content_type.nil? and !content_type.to_s.empty? and
content_type.split(';', 2)[0].downcase == YADIS_CONTENT_TYPE)
return resp.final_url
else
# Try the header
yadis_loc = resp[YADIS_HEADER_NAME.downcase]
if yadis_loc.nil?
# Parse as HTML if the header is missing.
#
# XXX: do we want to do something with content-type, like
# have a whitelist or a blacklist (for detecting that it's
# HTML)?
yadis_loc = Yadis.html_yadis_location(resp.body)
end
end
return yadis_loc
end
end
end
|