USER_AGENT = "python-openid/%s (%s)" % (openid.__version__, sys.platform)
MAX_RESPONSE_KB = 1024
    ALLOWED_TIME = 20  # seconds
def fetch(url, body=None, headers=None):
def createHTTPFetcher():
def getDefaultFetcher():
def setDefaultFetcher(fetcher, wrap_exceptions=True):
def usingCurl():
    def __init__(self, final_url=None, status=None, headers=None, body=None):
    def __repr__(self):
    def fetch(self, url, body=None, headers=None):
def _allowedURL(url):
    def __init__(self, why=None):
    def __init__(self, fetcher):
    def fetch(self, *args, **kwargs):
    def fetch(self, url, body=None, headers=None):
    def _makeResponse(self, urllib_response):
    def __init__(self):
    def _parseHeaders(self, header_file):
    def _checkURL(self, url):
    def fetch(self, url, body=None, headers=None):
                def write_data(chunk):
    def __init__(self, cache=None):
    def fetch(self, url, body=None, headers=None):
    def fetch(self, url, body=None, headers=None):
class HTTPResponse(object):
class HTTPFetcher(object):
class HTTPFetchingError(Exception):
class ExceptionWrappingFetcher(HTTPFetcher):
class Urllib2Fetcher(HTTPFetcher):
class HTTPError(HTTPFetchingError):
class CurlHTTPFetcher(HTTPFetcher):
class HTTPLib2Fetcher(HTTPFetcher):
class RequestsFetcher(HTTPFetcher): def urinorm(uri):
    """Return normalized URI.

    Normalization if performed according to RFC 3986, section 6 https://tools.ietf.org/html/rfc3986#section-6.
    Supported URIs are URLs and OpenID realm URIs.

    @type uri: six.text_type, six.binary_type deprecated
    @rtype: six.text_type
    @raise ValueError: If URI is invalid.
    """
    uri = string_to_text(uri, "Binary input for urinorm is deprecated. Use text input instead.")

    split_uri = urlsplit(uri)

    # Normalize scheme
    scheme = split_uri.scheme.lower()
    if scheme not in ('http', 'https'):
        raise ValueError('Not an absolute HTTP or HTTPS URI: {!r}'.format(uri))

    # Normalize netloc
    if not split_uri.netloc:
        raise ValueError('Not an absolute URI: {!r}'.format(uri))

    hostname = split_uri.hostname
    if hostname is None:
        hostname = ''
    else:
        hostname = hostname.lower()
    # Unquote percent encoded characters
    hostname = unquote(hostname)
    # Quote IDN domain names
    try:
        # hostname: str --[idna]--> bytes --[utf-8]--> str
        hostname = hostname.encode('idna').decode('utf-8')
    except ValueError as error:
        raise ValueError('Invalid hostname {!r}: {}'.format(hostname, error))
    _check_disallowed_characters(hostname, 'hostname')

    try:
        port = split_uri.port
    except ValueError as error:
        raise ValueError('Invalid port in {!r}: {}'.format(split_uri.netloc, error))
    if port is None:
        port = ''
    elif (scheme == 'http' and port == 80) or (scheme == 'https' and port == 443):
        port = ''

    netloc = hostname
    if port:
        netloc = netloc + ':' + six.text_type(port)
    userinfo_chunks = [i for i in (split_uri.username, split_uri.password) if i is not None]
    if userinfo_chunks:
        userinfo = ':'.join(userinfo_chunks)
        _check_disallowed_characters(userinfo, 'userinfo')
        netloc = userinfo + '@' + netloc

    # Normalize path
    path = split_uri.path
    # Unquote and quote - this normalizes the percent encoding

    # This is hackish. `unquote` and `quote` requires `str` in both py27 and py3+.
    if isinstance(path, str):
        # Python 3 branch
        path = quote(unquote(path), safe='/' + SUB_DELIMS)
    else:
        # Python 2 branch
        path = quote(unquote(path.encode('utf-8')), safe=('/' + SUB_DELIMS).encode('utf-8')).decode('utf-8')

    path = remove_dot_segments(path)
    if not path:
        path = '/'
    _check_disallowed_characters(path, 'path')

    # Normalize query.  On Python 2, `urlencode` without `doseq=True`
    # requires values to be convertible to native strings using `str()`.
    if isinstance(split_uri.query, str):
        # Python 3 branch
        data = parse_qsl(split_uri.query)
    else:
        # Python 2 branch
        data = parse_qsl(split_uri.query.encode('utf-8'))
    query = urlencode(data)
    _check_disallowed_characters(query, 'query')

    # Normalize fragment
    fragment = unquote(split_uri.fragment)
    _check_disallowed_characters(fragment, 'fragment')

    return urlunsplit((scheme, netloc, path, query, fragment)) OPENID1_NS = 'http://openid.net/signon/1.0' OPENID2_NS = 'http://specs.openid.net/auth/2.0' def string_to_text(value, deprecate_msg):
    """
    Return input string coverted to text string.

    If input is text, it is returned as is.
    If input is binary, it is decoded using UTF-8 to text.
    """
    assert isinstance(value, (six.text_type, six.binary_type))
    if isinstance(value, six.binary_type):
        warnings.warn(deprecate_msg, DeprecationWarning)
        value = value.decode('utf-8')
    return value class BasicServiceEndpoint(object):
class IFilter(object):
class TransformFilterMaker(object):
class CompoundFilter(object):
    def __init__(self, yadis_url, type_uris, uri, service_element):
    def matchTypes(self, type_uris):
    def fromBasicServiceEndpoint(endpoint):
    def getServiceEndpoints(self, yadis_url, service_element):
    def __init__(self, filter_functions):
    def getServiceEndpoints(self, yadis_url, service_element):
    def applyFilters(self, endpoint):
    def __init__(self, subfilters):
    def getServiceEndpoints(self, yadis_url, service_element):
def mkFilter(parts):
def mkCompoundFilter(parts): XRI_AUTHORITIES = ['!', '=', '@', '+', '$', '(']
def identifierScheme(identifier):
def toIRINormal(xri):
def _escape_xref(xref_match):
def escapeForIRI(xri):
def toURINormal(xri):
def iriToURI(iri):
def providerIsAuthoritative(providerID, canonicalID):
def rootAuthority(xri):
def XRI(xri): DEFAULT_PROXY = 'http://proxy.xri.net/'
class ProxyResolver(object):
    def __init__(self, proxy_url=DEFAULT_PROXY):
    def queryURL(self, xri, service_type=None):
    def query(self, xri, service_types):
def _appendArgs(url, args): class DiscoveryFailure(Exception):
    """Raised when a YADIS protocol error occurs in the discovery process"""
    identity_url = None

    def __init__(self, message, http_response):
        Exception.__init__(self, message)
        self.http_response = http_response def discover(uri):
    """Discover services for a given URI.

    @param 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.

    @return: DiscoveryResult object

    @raises Exception: Any exception that can be raised by fetching a URL with
        the given fetcher.
    @raises DiscoveryFailure: When the HTTP response does not have a 200 code.
    """
    result = DiscoveryResult(uri)
    resp = fetchers.fetch(uri, headers={'Accept': YADIS_ACCEPT_HEADER})
    if resp.status not in (200, 206):
        raise DiscoveryFailure(
            'HTTP Response status from identity URL host is not 200. '
            'Got status %r' % (resp.status,), resp)

    # 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.headers.get('content-type')

    result.xrds_uri = whereIsYadis(resp)

    if result.xrds_uri and result.usedYadisLocation():
        resp = fetchers.fetch(result.xrds_uri)
        if resp.status not in (200, 206):
            exc = DiscoveryFailure(
                'HTTP Response status from Yadis host is not 200. '
                'Got status %r' % (resp.status,), resp)
            exc.identity_url = result.normalized_uri
            raise exc
        result.content_type = resp.headers.get('content-type')

    result.response_text = resp.body
    return result XRD_NS_2_0 = 'xri://$xrd*($v*2.0)' class XRDSError(Exception):
    """An error with the XRDS document."""

    # The exception that triggered this exception
    reason = None def nsTag(ns, t):
    return '{%s}%s' % (ns, t) def applyFilter(normalized_uri, xrd_data, flt=None):
    """Generate an iterable of endpoint objects given this input data,
    presumably from the result of performing the Yadis protocol.

    @param normalized_uri: The input URL, after following redirects,
        as in the Yadis protocol.

    @param xrd_data: The XML text the XRDS file fetched from the
        normalized URI.
    @type xrd_data: six.binary_type
    """
    flt = mkFilter(flt)
    et = parseXRDS(xrd_data)

    endpoints = []
    for service_element in iterServices(et):
        endpoints.extend(
            flt.getServiceEndpoints(normalized_uri, service_element))

    return endpoints
import logging
import six
from lxml.etree import LxmlError
from lxml.html import document_fromstring
from six.moves.urllib.parse import urldefrag, urlparse
from openid import fetchers, urinorm
from openid.message import OPENID1_NS as OPENID_1_0_MESSAGE_NS, OPENID2_NS as OPENID_2_0_MESSAGE_NS
from openid.oidutil import string_to_text
from openid.yadis import filters, xri, xrires
from openid.yadis.discover import DiscoveryFailure, discover as yadisDiscover
from openid.yadis.etxrd import XRD_NS_2_0, XRDSError, nsTag
from openid.yadis.services import applyFilter as extractServices

def normalizeURL(url):
    """Normalize a URL, converting normalization failures to
    DiscoveryFailure"""
    try:
        normalized = urinorm.urinorm(url)
    except ValueError as why:
        raise DiscoveryFailure('Normalizing identifier: %s' % six.text_type(why), None)
    else:
        return urldefrag(normalized)[0]


def normalizeXRI(xri):
    """Normalize an XRI, stripping its scheme if present"""
    if xri.startswith("xri://"):
        xri = xri[6:]
    return xri


def arrangeByType(service_list, preferred_types):
    """Rearrange service_list in a new list so services are ordered by
    types listed in preferred_types.  Return the new list."""

    def bestMatchingService(service):
        """Return the index of the first matching type, or something
        higher if no type matches.

        This provides an ordering in which service elements that
        contain a type that comes earlier in the preferred types list
        come before service elements that come later. If a service
        element has more than one type, the most preferred one wins.
        """
        for i, t in enumerate(preferred_types):
            if preferred_types[i] in service.type_uris:
                return i

        return len(preferred_types)

    # Build a list with the service elements in tuples whose
    # comparison will prefer the one with the best matching service
    prio_services = sorted((bestMatchingService(s), orig_index, s) for (orig_index, s) in enumerate(service_list))

    # Now that the services are sorted by priority, remove the sort
    # keys from the list.
    for i in range(len(prio_services)):
        prio_services[i] = prio_services[i][2]

    return prio_services


def getOPOrUserServices(openid_services):
    """Extract OP Identifier services.  If none found, return the
    rest, sorted with most preferred first according to
    OpenIDServiceEndpoint.openid_type_uris.

    openid_services is a list of OpenIDServiceEndpoint objects.

    Returns a list of OpenIDServiceEndpoint objects."""

    op_services = arrangeByType(openid_services, [OPENID_IDP_2_0_TYPE])

    openid_services = arrangeByType(openid_services,
                                    OpenIDServiceEndpoint.openid_type_uris)

    return op_services or openid_services


def discoverYadis(uri):
    """Discover OpenID services for a URI. Tries Yadis and falls back
    on old-style <link rel='...'> discovery if Yadis fails.

    @param uri: normalized identity URL
    @type uri: six.text_type, six.binary_type is deprecated

    @return: (claimed_id, services)
    @rtype: (six.text_type, list(OpenIDServiceEndpoint))

    @raises DiscoveryFailure: when discovery fails.
    """
    uri = string_to_text(uri, "Binary values for discoverYadis are deprecated. Use text input instead.")
    # Might raise a yadis.discover.DiscoveryFailure if no document
    # came back for that URI at all.  I don't think falling back
    # to OpenID 1.0 discovery on the same URL will help, so don't
    # bother to catch it.
    response = yadisDiscover(uri)

    yadis_url = response.normalized_uri
    body = response.response_text
    try:
        openid_services = OpenIDServiceEndpoint.fromXRDS(yadis_url, body)
    except XRDSError:
        # Does not parse as a Yadis XRDS file
        openid_services = []

    if not openid_services:
        # Either not an XRDS or there are no OpenID services.

        if response.isXRDS():
            # if we got the Yadis content-type or followed the Yadis
            # header, re-fetch the document without following the Yadis
            # header, with no Accept header.
            return discoverNoYadis(uri)

        # Try to parse the response as HTML.
        # <link rel="...">
        openid_services = OpenIDServiceEndpoint.fromHTML(yadis_url, body)

    return (yadis_url, getOPOrUserServices(openid_services))


def discoverXRI(iname):
    endpoints = []
    iname = normalizeXRI(iname)
    try:
        canonicalID, services = xrires.ProxyResolver().query(
            iname, OpenIDServiceEndpoint.openid_type_uris)

        if canonicalID is None:
            raise XRDSError('No CanonicalID found for XRI %r' % (iname,))


        flt = filters.mkFilter(OpenIDServiceEndpoint)