~gary/python-openid/python-openid-2.2.1-patched

« back to all changes in this revision

Viewing changes to openid/yadis/discover.py

  • Committer: Launchpad Patch Queue Manager
  • Date: 2007-11-30 02:46:28 UTC
  • mfrom: (1.1.1 pyopenid-2.0)
  • Revision ID: launchpad@pqm.canonical.com-20071130024628-qktwsew3383iawmq
[rs=SteveA] upgrade to python-openid-2.0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- test-case-name: openid.test.test_yadis_discover -*-
 
2
__all__ = ['discover', 'DiscoveryResult', 'DiscoveryFailure']
 
3
 
 
4
from cStringIO import StringIO
 
5
 
 
6
from openid import fetchers
 
7
 
 
8
from openid.yadis.constants import \
 
9
     YADIS_HEADER_NAME, YADIS_CONTENT_TYPE, YADIS_ACCEPT_HEADER
 
10
from openid.yadis.parsehtml import MetaNotFound, findHTMLMeta
 
11
 
 
12
class DiscoveryFailure(Exception):
 
13
    """Raised when a YADIS protocol error occurs in the discovery process"""
 
14
    identity_url = None
 
15
 
 
16
    def __init__(self, message, http_response):
 
17
        Exception.__init__(self, message)
 
18
        self.http_response = http_response
 
19
 
 
20
class DiscoveryResult(object):
 
21
    """Contains the result of performing Yadis discovery on a URI"""
 
22
 
 
23
    # The URI that was passed to the fetcher
 
24
    request_uri = None
 
25
 
 
26
    # The result of following redirects from the request_uri
 
27
    normalized_uri = None
 
28
 
 
29
    # The URI from which the response text was returned (set to
 
30
    # None if there was no XRDS document found)
 
31
    xrds_uri = None
 
32
 
 
33
    # The content-type returned with the response_text
 
34
    content_type = None
 
35
 
 
36
    # The document returned from the xrds_uri
 
37
    response_text = None
 
38
 
 
39
    def __init__(self, request_uri):
 
40
        """Initialize the state of the object
 
41
 
 
42
        sets all attributes to None except the request_uri
 
43
        """
 
44
        self.request_uri = request_uri
 
45
 
 
46
    def usedYadisLocation(self):
 
47
        """Was the Yadis protocol's indirection used?"""
 
48
        return self.normalized_uri != self.xrds_uri
 
49
 
 
50
    def isXRDS(self):
 
51
        """Is the response text supposed to be an XRDS document?"""
 
52
        return (self.usedYadisLocation() or
 
53
                self.content_type == YADIS_CONTENT_TYPE)
 
54
 
 
55
def discover(uri):
 
56
    """Discover services for a given URI.
 
57
 
 
58
    @param uri: The identity URI as a well-formed http or https
 
59
        URI. The well-formedness and the protocol are not checked, but
 
60
        the results of this function are undefined if those properties
 
61
        do not hold.
 
62
 
 
63
    @return: DiscoveryResult object
 
64
 
 
65
    @raises Exception: Any exception that can be raised by fetching a URL with
 
66
        the given fetcher.
 
67
    """
 
68
    result = DiscoveryResult(uri)
 
69
    resp = fetchers.fetch(uri, headers={'Accept': YADIS_ACCEPT_HEADER})
 
70
    if resp.status != 200:
 
71
        raise DiscoveryFailure(
 
72
            'HTTP Response status from identity URL host is not 200. '
 
73
            'Got status %r' % (resp.status,), resp)
 
74
 
 
75
    # Note the URL after following redirects
 
76
    result.normalized_uri = resp.final_url
 
77
 
 
78
    # Attempt to find out where to go to discover the document
 
79
    # or if we already have it
 
80
    result.content_type = resp.headers.get('content-type')
 
81
 
 
82
    result.xrds_uri = whereIsYadis(resp)
 
83
 
 
84
    if result.xrds_uri and result.usedYadisLocation():
 
85
        resp = fetchers.fetch(result.xrds_uri)
 
86
        if resp.status != 200:
 
87
            exc = DiscoveryFailure(
 
88
                'HTTP Response status from Yadis host is not 200. '
 
89
                'Got status %r' % (resp.status,), resp)
 
90
            exc.identity_url = result.normalized_uri
 
91
            raise exc
 
92
        result.content_type = resp.headers.get('content-type')
 
93
 
 
94
    result.response_text = resp.body
 
95
    return result
 
96
 
 
97
 
 
98
 
 
99
def whereIsYadis(resp):
 
100
    """Given a HTTPResponse, return the location of the Yadis document.
 
101
 
 
102
    May be the URL just retrieved, another URL, or None, if I can't
 
103
    find any.
 
104
 
 
105
    [non-blocking]
 
106
 
 
107
    @returns: str or None
 
108
    """
 
109
    # Attempt to find out where to go to discover the document
 
110
    # or if we already have it
 
111
    content_type = resp.headers.get('content-type')
 
112
 
 
113
    # According to the spec, the content-type header must be an exact
 
114
    # match, or else we have to look for an indirection.
 
115
    if (content_type and
 
116
        content_type.split(';', 1)[0].lower() == YADIS_CONTENT_TYPE):
 
117
        return resp.final_url
 
118
    else:
 
119
        # Try the header
 
120
        yadis_loc = resp.headers.get(YADIS_HEADER_NAME.lower())
 
121
 
 
122
        if not yadis_loc:
 
123
            # Parse as HTML if the header is missing.
 
124
            #
 
125
            # XXX: do we want to do something with content-type, like
 
126
            # have a whitelist or a blacklist (for detecting that it's
 
127
            # HTML)?
 
128
            try:
 
129
                yadis_loc = findHTMLMeta(StringIO(resp.body))
 
130
            except MetaNotFound:
 
131
                pass
 
132
 
 
133
        return yadis_loc
 
134