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
|