summaryrefslogtreecommitdiffstats
path: root/vendor/gems/ruby-openid-2.1.4/lib/openid/yadis/discovery.rb
blob: 55d6f09b1c9bfafd21aff0b5e853e071edc6401b (plain)
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