webfinger.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. import logging
  2. import rd
  3. __version__ = '1.0'
  4. WEBFINGER_TYPE = 'application/jrd+json'
  5. LEGACY_WEBFINGER_TYPES = ['application/json']
  6. UNOFFICIAL_ENDPOINTS = {
  7. # 'facebook.com': 'facebook-webfinger.appspot.com',
  8. # 'twitter.com': 'twitter-webfinger.appspot.com',
  9. }
  10. logger = logging.getLogger("webfinger")
  11. class WebFingerException(Exception):
  12. pass
  13. class WebFingerClient(object):
  14. def __init__(self, timeout=None, official=False):
  15. self.official = official
  16. self.timeout = timeout
  17. def _parse_host(self, resource):
  18. (scheme, host, path) = rd.parse_uri_components(resource)
  19. if host in UNOFFICIAL_ENDPOINTS and not self.official:
  20. unofficial_host = UNOFFICIAL_ENDPOINTS[host]
  21. logger.debug('host %s is not supported, using unofficial endpoint %s' % (host, unofficial_host))
  22. host = unofficial_host
  23. return host
  24. def finger(self, resource, host=None, rel=None):
  25. import requests
  26. if not host:
  27. host = self._parse_host(resource)
  28. url = "https://%s/.well-known/webfinger" % host
  29. headers = {
  30. 'User-Agent': 'python-webfinger/%s' % __version__,
  31. 'Accept': ', '.join(rd.JRD_TYPES + rd.XRD_TYPES),
  32. }
  33. params = {'resource': resource}
  34. if rel:
  35. params['rel'] = rel
  36. resp = requests.get(url, params=params, headers=headers, timeout=self.timeout, verify=True)
  37. logger.debug('fetching RD from %s' % resp.url)
  38. content_type = resp.headers.get('Content-Type', '').split(';', 1)[0].strip()
  39. logger.debug('response content type: %s' % content_type)
  40. if content_type not in (rd.JRD_TYPES + rd.XRD_TYPES):
  41. raise WebFingerException('Invalid response type from server')
  42. ''' Returns an RD object '''
  43. return rd.loads(resp.content.decode(resp.encoding if resp.encoding else 'utf-8'), content_type)
  44. def finger(resource, rel=None):
  45. """ Convenience method for invoking WebFingerClient.
  46. """
  47. return WebFingerClient().finger(resource, rel=rel)
  48. if __name__ == '__main__':
  49. import argparse
  50. parser = argparse.ArgumentParser(description="Simple webfinger client.")
  51. parser.add_argument("acct", metavar="URI", help="account URI")
  52. parser.add_argument("-d", "--debug", dest="debug", action="store_true", help="print debug logging output to console")
  53. parser.add_argument("-r", "--rel", metavar="REL", dest="rel", help="desired relation")
  54. args = parser.parse_args()
  55. if args.debug:
  56. logging.basicConfig(level=logging.DEBUG)
  57. wf = finger(args.acct, rel=args.rel)
  58. print("--- %s ---" % wf.subject)
  59. if args.rel:
  60. link = wf.find_link(args.rel)
  61. if link is None:
  62. print("*** Link not found for rel=%s" % args.rel)
  63. else:
  64. print("%s:\n\t%s" % (args.rel, link))
  65. else:
  66. for rel in rd.KNOWN_RELS:
  67. link = getattr(wf, rel)
  68. if link:
  69. print('{}\t{}'.format(rel, link.href if link.href else link.template))