123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122 |
- # -*- test-case-name: openid.test.test_xri -*-
- """Utility functions for handling XRIs.
- @see: XRI Syntax v2.0 at the U{OASIS XRI Technical Committee<http://www.oasis-open.org/committees/tc_home.php?wg_abbrev=xri>}
- """
- import re
- from functools import reduce
- from openid import codecutil # registers 'oid_percent_escape' encoding handler
- XRI_AUTHORITIES = ['!', '=', '@', '+', '$', '(']
- def identifierScheme(identifier):
- """Determine if this identifier is an XRI or URI.
- @returns: C{"XRI"} or C{"URI"}
- """
- if identifier.startswith('xri://') or (identifier and
- identifier[0] in XRI_AUTHORITIES):
- return "XRI"
- else:
- return "URI"
- def toIRINormal(xri):
- """Transform an XRI to IRI-normal form."""
- if not xri.startswith('xri://'):
- xri = 'xri://' + xri
- return escapeForIRI(xri)
- _xref_re = re.compile(r'\((.*?)\)')
- def _escape_xref(xref_match):
- """Escape things that need to be escaped if they're in a cross-reference.
- """
- xref = xref_match.group()
- xref = xref.replace('/', '%2F')
- xref = xref.replace('?', '%3F')
- xref = xref.replace('#', '%23')
- return xref
- def escapeForIRI(xri):
- """Escape things that need to be escaped when transforming to an IRI."""
- xri = xri.replace('%', '%25')
- xri = _xref_re.sub(_escape_xref, xri)
- return xri
- def toURINormal(xri):
- """Transform an XRI to URI normal form."""
- return iriToURI(toIRINormal(xri))
- def iriToURI(iri):
- """Transform an IRI to a URI by escaping unicode."""
- # According to RFC 3987, section 3.1, "Mapping of IRIs to URIs"
- if isinstance(iri, bytes):
- iri = str(iri, encoding="utf-8")
- return iri.encode('ascii', errors='oid_percent_escape').decode()
- def providerIsAuthoritative(providerID, canonicalID):
- """Is this provider ID authoritative for this XRI?
- @returntype: bool
- """
- # XXX: can't use rsplit until we require python >= 2.4.
- lastbang = canonicalID.rindex('!')
- parent = canonicalID[:lastbang]
- return parent == providerID
- def rootAuthority(xri):
- """Return the root authority for an XRI.
- Example::
- rootAuthority("xri://@example") == "xri://@"
- @type xri: unicode
- @returntype: unicode
- """
- if xri.startswith('xri://'):
- xri = xri[6:]
- authority = xri.split('/', 1)[0]
- if authority[0] == '(':
- # Cross-reference.
- # XXX: This is incorrect if someone nests cross-references so there
- # is another close-paren in there. Hopefully nobody does that
- # before we have a real xriparse function. Hopefully nobody does
- # that *ever*.
- root = authority[:authority.index(')') + 1]
- elif authority[0] in XRI_AUTHORITIES:
- # Other XRI reference.
- root = authority[0]
- else:
- # IRI reference. XXX: Can IRI authorities have segments?
- segments = authority.split('!')
- segments = reduce(list.__add__, [s.split('*') for s in segments])
- root = segments[0]
- return XRI(root)
- def XRI(xri):
- """An XRI object allowing comparison of XRI.
- Ideally, this would do full normalization and provide comparsion
- operators as per XRI Syntax. Right now, it just does a bit of
- canonicalization by ensuring the xri scheme is present.
- @param xri: an xri string
- @type xri: unicode
- """
- if not xri.startswith('xri://'):
- xri = 'xri://' + xri
- return xri
|