| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  | __filename__ = "webfinger.py" | 
					
						
							|  |  |  |  | __author__ = "Bob Mottram" | 
					
						
							|  |  |  |  | __license__ = "AGPL3+" | 
					
						
							|  |  |  |  | __version__ = "1.1.0" | 
					
						
							|  |  |  |  | __maintainer__ = "Bob Mottram" | 
					
						
							|  |  |  |  | __email__ = "bob@freedombone.net" | 
					
						
							|  |  |  |  | __status__ = "Production" | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | import base64 | 
					
						
							| 
									
										
										
										
											2020-03-04 09:59:08 +00:00
										 |  |  |  | try: | 
					
						
							|  |  |  |  |     from Cryptodome.PublicKey import RSA | 
					
						
							|  |  |  |  |     from Cryptodome.Util import number | 
					
						
							|  |  |  |  | except ImportError: | 
					
						
							|  |  |  |  |     from Crypto.PublicKey import RSA | 
					
						
							|  |  |  |  |     from Crypto.Util import number | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |  | import os | 
					
						
							| 
									
										
										
										
											2020-04-15 11:10:30 +00:00
										 |  |  |  | import urllib.parse | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |  | from session import getJson | 
					
						
							| 
									
										
										
										
											2019-06-30 15:03:26 +00:00
										 |  |  |  | from cache import storeWebfingerInCache | 
					
						
							|  |  |  |  | from cache import getWebfingerFromCache | 
					
						
							| 
									
										
										
										
											2020-12-16 11:19:16 +00:00
										 |  |  |  | from utils import getFullDomain | 
					
						
							| 
									
										
										
										
											2019-10-22 11:55:06 +00:00
										 |  |  |  | from utils import loadJson | 
					
						
							| 
									
										
										
										
											2020-03-02 14:35:44 +00:00
										 |  |  |  | from utils import loadJsonOnionify | 
					
						
							| 
									
										
										
										
											2019-10-22 11:55:06 +00:00
										 |  |  |  | from utils import saveJson | 
					
						
							| 
									
										
										
										
											2020-06-11 12:26:15 +00:00
										 |  |  |  | from utils import getProtocolPrefixes | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | def parseHandle(handle: str) -> (str, str): | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |  |     if '.' not in handle: | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  |         return None, None | 
					
						
							| 
									
										
										
										
											2020-06-11 12:26:15 +00:00
										 |  |  |  |     prefixes = getProtocolPrefixes() | 
					
						
							| 
									
										
										
										
											2020-06-11 12:16:45 +00:00
										 |  |  |  |     handleStr = handle | 
					
						
							|  |  |  |  |     for prefix in prefixes: | 
					
						
							|  |  |  |  |         handleStr = handleStr.replace(prefix, '') | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |  |     if '/@' in handle: | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  |         domain, nickname = handleStr.split('/@') | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |  |     else: | 
					
						
							|  |  |  |  |         if '/users/' in handle: | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  |             domain, nickname = handleStr.split('/users/') | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |  |         else: | 
					
						
							|  |  |  |  |             if '@' in handle: | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  |                 nickname, domain = handle.split('@') | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |  |             else: | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  |                 return None, None | 
					
						
							|  |  |  |  |     return nickname, domain | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  | def webfingerHandle(session, handle: str, httpPrefix: str, | 
					
						
							|  |  |  |  |                     cachedWebfingers: {}, | 
					
						
							|  |  |  |  |                     fromDomain: str, projectVersion: str) -> {}: | 
					
						
							| 
									
										
										
										
											2020-06-23 10:41:12 +00:00
										 |  |  |  |     """Gets webfinger result for the given ActivityPub handle
 | 
					
						
							| 
									
										
										
										
											2020-05-07 13:21:58 +00:00
										 |  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2019-07-16 10:19:04 +00:00
										 |  |  |  |     if not session: | 
					
						
							|  |  |  |  |         print('WARN: No session specified for webfingerHandle') | 
					
						
							|  |  |  |  |         return None | 
					
						
							| 
									
										
										
										
											2019-07-19 13:32:58 +00:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  |     nickname, domain = parseHandle(handle) | 
					
						
							| 
									
										
										
										
											2019-07-03 09:40:27 +00:00
										 |  |  |  |     if not nickname: | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |  |         return None | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  |     wfDomain = domain | 
					
						
							| 
									
										
										
										
											2019-07-01 21:01:43 +00:00
										 |  |  |  |     if ':' in wfDomain: | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  |         # wfPortStr=wfDomain.split(':')[1] | 
					
						
							|  |  |  |  |         # if wfPortStr.isdigit(): | 
					
						
							|  |  |  |  |         #     wfPort=int(wfPortStr) | 
					
						
							|  |  |  |  |         # if wfPort==80 or wfPort==443: | 
					
						
							|  |  |  |  |         wfDomain = wfDomain.split(':')[0] | 
					
						
							|  |  |  |  |     wf = getWebfingerFromCache(nickname + '@' + wfDomain, | 
					
						
							|  |  |  |  |                                cachedWebfingers) | 
					
						
							| 
									
										
										
										
											2019-06-30 15:03:26 +00:00
										 |  |  |  |     if wf: | 
					
						
							| 
									
										
										
										
											2020-03-22 21:16:02 +00:00
										 |  |  |  |         return wf | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  |     url = '{}://{}/.well-known/webfinger'.format(httpPrefix, domain) | 
					
						
							|  |  |  |  |     par = { | 
					
						
							|  |  |  |  |         'resource': 'acct:{}'.format(nickname + '@' + wfDomain) | 
					
						
							| 
									
										
										
										
											2020-03-22 20:36:19 +00:00
										 |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  |     hdr = { | 
					
						
							| 
									
										
										
										
											2020-03-22 20:36:19 +00:00
										 |  |  |  |         'Accept': 'application/jrd+json' | 
					
						
							|  |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-07-04 17:31:41 +00:00
										 |  |  |  |     try: | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  |         result = \ | 
					
						
							|  |  |  |  |             getJson(session, url, hdr, par, projectVersion, | 
					
						
							|  |  |  |  |                     httpPrefix, fromDomain) | 
					
						
							| 
									
										
										
										
											2019-08-14 22:24:51 +00:00
										 |  |  |  |     except Exception as e: | 
					
						
							|  |  |  |  |         print(e) | 
					
						
							| 
									
										
										
										
											2019-07-04 17:31:41 +00:00
										 |  |  |  |         return None | 
					
						
							| 
									
										
										
										
											2020-05-07 13:21:58 +00:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     if result: | 
					
						
							|  |  |  |  |         storeWebfingerInCache(nickname + '@' + wfDomain, | 
					
						
							|  |  |  |  |                               result, cachedWebfingers) | 
					
						
							| 
									
										
										
										
											2020-05-07 13:26:55 +00:00
										 |  |  |  |     else: | 
					
						
							|  |  |  |  |         print("WARN: Unable to webfinger " + url + ' ' + | 
					
						
							|  |  |  |  |               'nickname: ' + str(nickname) + ' ' + | 
					
						
							|  |  |  |  |               'domain: ' + str(wfDomain) + ' ' + | 
					
						
							|  |  |  |  |               'headers: ' + str(hdr) + ' ' + | 
					
						
							|  |  |  |  |               'params: ' + str(par)) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |  |     return result | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-01 11:09:09 +00:00
										 |  |  |  | def generateMagicKey(publicKeyPem) -> str: | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |  |     """See magic_key method in
 | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  |        https://github.com/tootsuite/mastodon/blob/ | 
					
						
							|  |  |  |  |        707ddf7808f90e3ab042d7642d368c2ce8e95e6f/app/models/account.rb | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  |     privkey = RSA.importKey(publicKeyPem) | 
					
						
							|  |  |  |  |     modBytes = number.long_to_bytes(privkey.n) | 
					
						
							|  |  |  |  |     mod = base64.urlsafe_b64encode(modBytes).decode("utf-8") | 
					
						
							|  |  |  |  |     expBytes = number.long_to_bytes(privkey.e) | 
					
						
							|  |  |  |  |     pubexp = base64.urlsafe_b64encode(expBytes).decode("utf-8") | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |  |     return f"data:application/magic-public-key,RSA.{mod}.{pubexp}" | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | def storeWebfingerEndpoint(nickname: str, domain: str, port: int, | 
					
						
							|  |  |  |  |                            baseDir: str, wfJson: {}) -> bool: | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |  |     """Stores webfinger endpoint for a user to a file
 | 
					
						
							|  |  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  |     originalDomain = domain | 
					
						
							| 
									
										
										
										
											2020-12-16 11:19:16 +00:00
										 |  |  |  |     domain = getFullDomain(domain, port) | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  |     handle = nickname + '@' + domain | 
					
						
							|  |  |  |  |     wfSubdir = '/wfendpoints' | 
					
						
							|  |  |  |  |     if not os.path.isdir(baseDir + wfSubdir): | 
					
						
							|  |  |  |  |         os.mkdir(baseDir + wfSubdir) | 
					
						
							| 
									
										
										
										
											2020-09-15 09:16:03 +00:00
										 |  |  |  |     filename = baseDir + wfSubdir + '/' + handle + '.json' | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  |     saveJson(wfJson, filename) | 
					
						
							|  |  |  |  |     if nickname == 'inbox': | 
					
						
							|  |  |  |  |         handle = originalDomain + '@' + domain | 
					
						
							| 
									
										
										
										
											2020-09-15 09:16:03 +00:00
										 |  |  |  |         filename = baseDir + wfSubdir + '/' + handle + '.json' | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  |         saveJson(wfJson, filename) | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |  |     return True | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | def createWebfingerEndpoint(nickname: str, domain: str, port: int, | 
					
						
							|  |  |  |  |                             httpPrefix: str, publicKeyPem) -> {}: | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |  |     """Creates a webfinger endpoint for a user
 | 
					
						
							|  |  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  |     originalDomain = domain | 
					
						
							| 
									
										
										
										
											2020-12-16 11:19:16 +00:00
										 |  |  |  |     domain = getFullDomain(domain, port) | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     personName = nickname | 
					
						
							|  |  |  |  |     personId = httpPrefix + "://" + domain + "/users/" + personName | 
					
						
							|  |  |  |  |     subjectStr = "acct:" + personName + "@" + originalDomain | 
					
						
							|  |  |  |  |     profilePageHref = httpPrefix + "://" + domain + "/@" + nickname | 
					
						
							|  |  |  |  |     if nickname == 'inbox' or nickname == originalDomain: | 
					
						
							|  |  |  |  |         personName = 'actor' | 
					
						
							|  |  |  |  |         personId = httpPrefix + "://" + domain + "/" + personName | 
					
						
							|  |  |  |  |         subjectStr = "acct:" + originalDomain + "@" + originalDomain | 
					
						
							|  |  |  |  |         profilePageHref = httpPrefix + '://' + domain + \ | 
					
						
							|  |  |  |  |             '/about/more?instance_actor=true' | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     actor = httpPrefix + "://" + domain + "/users/" + nickname | 
					
						
							|  |  |  |  |     account = { | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |  |         "aliases": [ | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  |             httpPrefix + "://" + domain + "/@" + personName, | 
					
						
							| 
									
										
										
										
											2019-08-23 20:03:06 +00:00
										 |  |  |  |             personId | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |  |         ], | 
					
						
							|  |  |  |  |         "links": [ | 
					
						
							|  |  |  |  |             { | 
					
						
							| 
									
										
										
										
											2019-08-26 15:20:14 +00:00
										 |  |  |  |                 "href": profilePageHref, | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |  |                 "rel": "http://webfinger.net/rel/profile-page", | 
					
						
							|  |  |  |  |                 "type": "text/html" | 
					
						
							|  |  |  |  |             }, | 
					
						
							| 
									
										
										
										
											2019-08-26 14:30:09 +00:00
										 |  |  |  |             { | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  |                 "href": actor + ".atom", | 
					
						
							| 
									
										
										
										
											2019-08-26 14:30:09 +00:00
										 |  |  |  |                 "rel": "http://schemas.google.com/g/2010#updates-from", | 
					
						
							|  |  |  |  |                 "type": "application/atom+xml" | 
					
						
							|  |  |  |  |             }, | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |  |             { | 
					
						
							| 
									
										
										
										
											2019-08-23 20:03:06 +00:00
										 |  |  |  |                 "href": personId, | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |  |                 "rel": "self", | 
					
						
							|  |  |  |  |                 "type": "application/activity+json" | 
					
						
							|  |  |  |  |             }, | 
					
						
							|  |  |  |  |             { | 
					
						
							|  |  |  |  |                 "href": generateMagicKey(publicKeyPem), | 
					
						
							|  |  |  |  |                 "rel": "magic-public-key" | 
					
						
							|  |  |  |  |             } | 
					
						
							|  |  |  |  |         ], | 
					
						
							| 
									
										
										
										
											2019-08-23 20:05:16 +00:00
										 |  |  |  |         "subject": subjectStr | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |  |     } | 
					
						
							|  |  |  |  |     return account | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | def webfingerNodeInfo(httpPrefix: str, domainFull: str) -> {}: | 
					
						
							| 
									
										
										
										
											2019-11-13 10:32:12 +00:00
										 |  |  |  |     """ /.well-known/nodeinfo endpoint
 | 
					
						
							|  |  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  |     nodeinfo = { | 
					
						
							| 
									
										
										
										
											2019-11-13 10:32:12 +00:00
										 |  |  |  |         'links': [ | 
					
						
							|  |  |  |  |             { | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  |                 'href': httpPrefix + '://' + domainFull + '/nodeinfo/2.0', | 
					
						
							| 
									
										
										
										
											2019-11-13 10:32:12 +00:00
										 |  |  |  |                 'rel': 'http://nodeinfo.diaspora.software/ns/schema/2.0' | 
					
						
							|  |  |  |  |             } | 
					
						
							|  |  |  |  |         ] | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |     return nodeinfo | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | def webfingerMeta(httpPrefix: str, domainFull: str) -> str: | 
					
						
							| 
									
										
										
										
											2019-08-16 20:52:55 +00:00
										 |  |  |  |     """Return /.well-known/host-meta
 | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  |     metaStr = "<?xml version=’1.0' encoding=’UTF-8'?>" | 
					
						
							|  |  |  |  |     metaStr += "<XRD xmlns=’http://docs.oasis-open.org/ns/xri/xrd-1.0'" | 
					
						
							|  |  |  |  |     metaStr += " xmlns:hm=’http://host-meta.net/xrd/1.0'>" | 
					
						
							|  |  |  |  |     metaStr += "" | 
					
						
							|  |  |  |  |     metaStr += "<hm:Host>" + domainFull + "</hm:Host>" | 
					
						
							|  |  |  |  |     metaStr += "" | 
					
						
							|  |  |  |  |     metaStr += "<Link rel=’lrdd’" | 
					
						
							|  |  |  |  |     metaStr += " template=’" + httpPrefix + "://" + domainFull + \ | 
					
						
							|  |  |  |  |         "/describe?uri={uri}'>" | 
					
						
							|  |  |  |  |     metaStr += " <Title>Resource Descriptor</Title>" | 
					
						
							|  |  |  |  |     metaStr += " </Link>" | 
					
						
							|  |  |  |  |     metaStr += "</XRD>" | 
					
						
							| 
									
										
										
										
											2019-11-13 10:32:12 +00:00
										 |  |  |  |     return metaStr | 
					
						
							| 
									
										
										
										
											2019-08-16 20:52:55 +00:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | def webfingerLookup(path: str, baseDir: str, | 
					
						
							|  |  |  |  |                     domain: str, onionDomain: str, | 
					
						
							|  |  |  |  |                     port: int, debug: bool) -> {}: | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |  |     """Lookup the webfinger endpoint for an account
 | 
					
						
							|  |  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2020-03-22 21:16:02 +00:00
										 |  |  |  |     if not path.startswith('/.well-known/webfinger?'): | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |  |         return None | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  |     handle = None | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |  |     if 'resource=acct:' in path: | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  |         handle = path.split('resource=acct:')[1].strip() | 
					
						
							| 
									
										
										
										
											2019-07-19 14:19:36 +00:00
										 |  |  |  |         if debug: | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  |             print('DEBUG: WEBFINGER handle ' + handle) | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |  |     else: | 
					
						
							|  |  |  |  |         if 'resource=acct%3A' in path: | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  |             handle = path.split('resource=acct%3A')[1] | 
					
						
							| 
									
										
										
										
											2020-04-15 11:10:30 +00:00
										 |  |  |  |             handle = urllib.parse.unquote(handle.strip()) | 
					
						
							| 
									
										
										
										
											2019-07-19 14:19:36 +00:00
										 |  |  |  |             if debug: | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  |                 print('DEBUG: WEBFINGER handle ' + handle) | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |  |     if not handle: | 
					
						
							| 
									
										
										
										
											2019-07-19 14:19:36 +00:00
										 |  |  |  |         if debug: | 
					
						
							|  |  |  |  |             print('DEBUG: WEBFINGER handle missing') | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |  |         return None | 
					
						
							|  |  |  |  |     if '&' in handle: | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  |         handle = handle.split('&')[0].strip() | 
					
						
							| 
									
										
										
										
											2019-07-19 14:19:36 +00:00
										 |  |  |  |         if debug: | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  |             print('DEBUG: WEBFINGER handle with & removed ' + handle) | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |  |     if '@' not in handle: | 
					
						
							| 
									
										
										
										
											2019-07-19 14:19:36 +00:00
										 |  |  |  |         if debug: | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  |             print('DEBUG: WEBFINGER no @ in handle ' + handle) | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |  |         return None | 
					
						
							| 
									
										
										
										
											2020-12-16 11:19:16 +00:00
										 |  |  |  |     handle = getFullDomain(handle, port) | 
					
						
							| 
									
										
										
										
											2019-08-23 14:18:31 +00:00
										 |  |  |  |     # convert @domain@domain to inbox@domain | 
					
						
							|  |  |  |  |     if '@' in handle: | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  |         handleDomain = handle.split('@')[1] | 
					
						
							|  |  |  |  |         if handle.startswith(handleDomain + '@'): | 
					
						
							|  |  |  |  |             handle = 'inbox@' + handleDomain | 
					
						
							| 
									
										
										
										
											2020-03-02 14:35:44 +00:00
										 |  |  |  |     # if this is a lookup for a handle using its onion domain | 
					
						
							|  |  |  |  |     # then swap the onion domain for the clearnet version | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  |     onionify = False | 
					
						
							| 
									
										
										
										
											2020-03-02 14:35:44 +00:00
										 |  |  |  |     if onionDomain: | 
					
						
							|  |  |  |  |         if onionDomain in handle: | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  |             handle = handle.replace(onionDomain, domain) | 
					
						
							|  |  |  |  |             onionify = True | 
					
						
							| 
									
										
										
										
											2020-09-15 09:16:03 +00:00
										 |  |  |  |     filename = baseDir + '/wfendpoints/' + handle + '.json' | 
					
						
							| 
									
										
										
										
											2019-07-19 14:19:36 +00:00
										 |  |  |  |     if debug: | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  |         print('DEBUG: WEBFINGER filename ' + filename) | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |  |     if not os.path.isfile(filename): | 
					
						
							| 
									
										
										
										
											2019-07-19 14:19:36 +00:00
										 |  |  |  |         if debug: | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  |             print('DEBUG: WEBFINGER filename not found ' + filename) | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |  |         return None | 
					
						
							| 
									
										
										
										
											2020-03-02 14:35:44 +00:00
										 |  |  |  |     if not onionify: | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  |         wfJson = loadJson(filename) | 
					
						
							| 
									
										
										
										
											2020-03-02 14:35:44 +00:00
										 |  |  |  |     else: | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  |         print('Webfinger request for onionified ' + handle) | 
					
						
							|  |  |  |  |         wfJson = loadJsonOnionify(filename, domain, onionDomain) | 
					
						
							| 
									
										
										
										
											2019-10-22 11:55:06 +00:00
										 |  |  |  |     if not wfJson: | 
					
						
							| 
									
										
										
										
											2020-04-04 14:14:25 +00:00
										 |  |  |  |         wfJson = {"nickname": "unknown"} | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |  |     return wfJson | 
					
						
							| 
									
										
										
										
											2020-05-04 13:58:24 +00:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | def webfingerUpdateFromProfile(wfJson: {}, actorJson: {}) -> bool: | 
					
						
							|  |  |  |  |     """Updates webfinger Email/blog/xmpp links from profile
 | 
					
						
							|  |  |  |  |     Returns true if one or more tags has been changed | 
					
						
							|  |  |  |  |     """
 | 
					
						
							|  |  |  |  |     if not actorJson.get('attachment'): | 
					
						
							|  |  |  |  |         return False | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     changed = False | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     webfingerPropertyName = { | 
					
						
							|  |  |  |  |         "xmpp": "xmpp", | 
					
						
							| 
									
										
										
										
											2020-05-04 14:10:27 +00:00
										 |  |  |  |         "matrix": "matrix", | 
					
						
							| 
									
										
										
										
											2020-05-04 13:58:24 +00:00
										 |  |  |  |         "email": "mailto", | 
					
						
							|  |  |  |  |         "ssb": "ssb", | 
					
						
							|  |  |  |  |         "tox": "toxId" | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     for propertyValue in actorJson['attachment']: | 
					
						
							|  |  |  |  |         if not propertyValue.get('name'): | 
					
						
							|  |  |  |  |             continue | 
					
						
							|  |  |  |  |         propertyName = propertyValue['name'].lower() | 
					
						
							|  |  |  |  |         if not (propertyName.startswith('ssb') or | 
					
						
							|  |  |  |  |                 propertyName.startswith('xmpp') or | 
					
						
							| 
									
										
										
										
											2020-05-04 14:10:27 +00:00
										 |  |  |  |                 propertyName.startswith('matrix') or | 
					
						
							| 
									
										
										
										
											2020-05-04 13:58:24 +00:00
										 |  |  |  |                 propertyName.startswith('email') or | 
					
						
							|  |  |  |  |                 propertyName.startswith('tox')): | 
					
						
							|  |  |  |  |             continue | 
					
						
							|  |  |  |  |         if not propertyValue.get('type'): | 
					
						
							|  |  |  |  |             continue | 
					
						
							|  |  |  |  |         if not propertyValue.get('value'): | 
					
						
							|  |  |  |  |             continue | 
					
						
							|  |  |  |  |         if propertyValue['type'] != 'PropertyValue': | 
					
						
							|  |  |  |  |             continue | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         newValue = propertyValue['value'].strip() | 
					
						
							|  |  |  |  |         aliasIndex = 0 | 
					
						
							|  |  |  |  |         found = False | 
					
						
							|  |  |  |  |         for alias in wfJson['aliases']: | 
					
						
							|  |  |  |  |             if alias.startswith(webfingerPropertyName[propertyName] + ':'): | 
					
						
							|  |  |  |  |                 found = True | 
					
						
							|  |  |  |  |                 break | 
					
						
							|  |  |  |  |             aliasIndex += 1 | 
					
						
							|  |  |  |  |         newAlias = webfingerPropertyName[propertyName] + ':' + newValue | 
					
						
							|  |  |  |  |         if found: | 
					
						
							|  |  |  |  |             if wfJson['aliases'][aliasIndex] != newAlias: | 
					
						
							|  |  |  |  |                 changed = True | 
					
						
							|  |  |  |  |                 wfJson['aliases'][aliasIndex] = newAlias | 
					
						
							|  |  |  |  |         else: | 
					
						
							|  |  |  |  |             wfJson['aliases'].append(newAlias) | 
					
						
							|  |  |  |  |             changed = True | 
					
						
							|  |  |  |  |     return changed | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | def webfingerUpdate(baseDir: str, nickname: str, domain: str, | 
					
						
							|  |  |  |  |                     onionDomain: str, | 
					
						
							|  |  |  |  |                     cachedWebfingers: {}) -> None: | 
					
						
							|  |  |  |  |     handle = nickname + '@' + domain | 
					
						
							|  |  |  |  |     wfSubdir = '/wfendpoints' | 
					
						
							|  |  |  |  |     if not os.path.isdir(baseDir + wfSubdir): | 
					
						
							|  |  |  |  |         return | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-15 09:16:03 +00:00
										 |  |  |  |     filename = baseDir + wfSubdir + '/' + handle + '.json' | 
					
						
							| 
									
										
										
										
											2020-05-04 13:58:24 +00:00
										 |  |  |  |     onionify = False | 
					
						
							|  |  |  |  |     if onionDomain: | 
					
						
							|  |  |  |  |         if onionDomain in handle: | 
					
						
							|  |  |  |  |             handle = handle.replace(onionDomain, domain) | 
					
						
							|  |  |  |  |             onionify = True | 
					
						
							|  |  |  |  |     if not onionify: | 
					
						
							|  |  |  |  |         wfJson = loadJson(filename) | 
					
						
							|  |  |  |  |     else: | 
					
						
							|  |  |  |  |         wfJson = loadJsonOnionify(filename, domain, onionDomain) | 
					
						
							|  |  |  |  |     if not wfJson: | 
					
						
							|  |  |  |  |         return | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-15 09:16:03 +00:00
										 |  |  |  |     actorFilename = baseDir + '/accounts/' + handle + '.json' | 
					
						
							| 
									
										
										
										
											2020-05-04 13:58:24 +00:00
										 |  |  |  |     actorJson = loadJson(actorFilename) | 
					
						
							|  |  |  |  |     if not actorJson: | 
					
						
							|  |  |  |  |         return | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     if webfingerUpdateFromProfile(wfJson, actorJson): | 
					
						
							|  |  |  |  |         if saveJson(wfJson, filename): | 
					
						
							|  |  |  |  |             cachedWebfingers[handle] = wfJson |