2020-04-04 14:14:25 +00:00
|
|
|
|
__filename__ = "webfinger.py"
|
|
|
|
|
__author__ = "Bob Mottram"
|
|
|
|
|
__license__ = "AGPL3+"
|
2021-01-26 10:07:42 +00:00
|
|
|
|
__version__ = "1.2.0"
|
2020-04-04 14:14:25 +00:00
|
|
|
|
__maintainer__ = "Bob Mottram"
|
2021-09-10 16:14:50 +00:00
|
|
|
|
__email__ = "bob@libreserver.org"
|
2020-04-04 14:14:25 +00:00
|
|
|
|
__status__ = "Production"
|
2021-06-15 15:08:12 +00:00
|
|
|
|
__module_group__ = "ActivityPub"
|
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
|
2021-06-26 14:21:24 +00:00
|
|
|
|
from utils import removeDomainPort
|
2021-07-29 22:41:27 +00:00
|
|
|
|
from utils import getUserPaths
|
2021-07-30 13:00:23 +00:00
|
|
|
|
from utils import getGroupPaths
|
2021-08-14 11:13:39 +00:00
|
|
|
|
from utils import localActorUrl
|
2019-06-28 18:55:29 +00:00
|
|
|
|
|
2020-04-04 14:14:25 +00:00
|
|
|
|
|
2021-07-30 13:00:23 +00:00
|
|
|
|
def _parseHandle(handle: str) -> (str, str, bool):
|
2021-07-29 22:41:27 +00:00
|
|
|
|
"""Parses a handle and returns nickname and domain
|
|
|
|
|
"""
|
2021-07-30 13:00:23 +00:00
|
|
|
|
groupAccount = False
|
2019-06-28 18:55:29 +00:00
|
|
|
|
if '.' not in handle:
|
2021-07-30 13:00:23 +00:00
|
|
|
|
return None, None, False
|
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, '')
|
2021-07-29 22:41:27 +00:00
|
|
|
|
|
|
|
|
|
# try domain/@nick
|
2019-06-28 18:55:29 +00:00
|
|
|
|
if '/@' in handle:
|
2020-04-04 14:14:25 +00:00
|
|
|
|
domain, nickname = handleStr.split('/@')
|
2021-07-30 13:00:23 +00:00
|
|
|
|
return nickname, domain, False
|
2021-07-29 22:41:27 +00:00
|
|
|
|
|
|
|
|
|
# try nick@domain
|
|
|
|
|
if '@' in handle:
|
2021-07-30 13:00:23 +00:00
|
|
|
|
if handle.startswith('!'):
|
|
|
|
|
handle = handle[1:]
|
|
|
|
|
groupAccount = True
|
2021-07-29 22:41:27 +00:00
|
|
|
|
nickname, domain = handle.split('@')
|
2021-07-30 13:00:23 +00:00
|
|
|
|
return nickname, domain, groupAccount
|
2021-07-29 22:41:27 +00:00
|
|
|
|
|
|
|
|
|
# try for different /users/ paths
|
|
|
|
|
usersPaths = getUserPaths()
|
2021-07-30 13:00:23 +00:00
|
|
|
|
groupPaths = getGroupPaths()
|
2021-07-29 22:41:27 +00:00
|
|
|
|
for possibleUsersPath in usersPaths:
|
|
|
|
|
if possibleUsersPath in handle:
|
2021-07-30 13:00:23 +00:00
|
|
|
|
if possibleUsersPath in groupPaths:
|
|
|
|
|
groupAccount = True
|
2021-07-29 22:41:27 +00:00
|
|
|
|
domain, nickname = handleStr.split(possibleUsersPath)
|
2021-07-30 13:00:23 +00:00
|
|
|
|
return nickname, domain, groupAccount
|
2021-07-29 22:41:27 +00:00
|
|
|
|
|
2021-07-30 13:00:23 +00:00
|
|
|
|
return None, None, False
|
2019-06-28 18:55:29 +00:00
|
|
|
|
|
|
|
|
|
|
2020-04-04 14:14:25 +00:00
|
|
|
|
def webfingerHandle(session, handle: str, httpPrefix: str,
|
|
|
|
|
cachedWebfingers: {},
|
2021-03-14 19:22:58 +00:00
|
|
|
|
fromDomain: str, projectVersion: str,
|
2021-08-31 14:17:11 +00:00
|
|
|
|
debug: bool, groupAccount: bool,
|
|
|
|
|
signingPrivateKeyPem: 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:
|
2021-03-14 19:22:58 +00:00
|
|
|
|
if debug:
|
|
|
|
|
print('WARN: No session specified for webfingerHandle')
|
2019-07-16 10:19:04 +00:00
|
|
|
|
return None
|
2019-07-19 13:32:58 +00:00
|
|
|
|
|
2021-07-30 13:00:23 +00:00
|
|
|
|
nickname, domain, grpAccount = _parseHandle(handle)
|
2019-07-03 09:40:27 +00:00
|
|
|
|
if not nickname:
|
2019-06-28 18:55:29 +00:00
|
|
|
|
return None
|
2021-06-23 21:31:50 +00:00
|
|
|
|
wfDomain = removeDomainPort(domain)
|
|
|
|
|
|
2021-07-29 22:48:31 +00:00
|
|
|
|
wfHandle = nickname + '@' + wfDomain
|
|
|
|
|
wf = getWebfingerFromCache(wfHandle, cachedWebfingers)
|
2019-06-30 15:03:26 +00:00
|
|
|
|
if wf:
|
2021-03-14 19:22:58 +00:00
|
|
|
|
if debug:
|
|
|
|
|
print('Webfinger from cache: ' + str(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)
|
|
|
|
|
hdr = {
|
2020-03-22 20:36:19 +00:00
|
|
|
|
'Accept': 'application/jrd+json'
|
|
|
|
|
}
|
2021-11-18 17:14:59 +00:00
|
|
|
|
par = {
|
|
|
|
|
'resource': 'acct:{}'.format(wfHandle)
|
|
|
|
|
}
|
2019-07-04 17:31:41 +00:00
|
|
|
|
try:
|
2020-04-04 14:14:25 +00:00
|
|
|
|
result = \
|
2021-08-31 14:17:11 +00:00
|
|
|
|
getJson(signingPrivateKeyPem, session, url, hdr, par,
|
|
|
|
|
debug, projectVersion, httpPrefix, fromDomain)
|
2019-08-14 22:24:51 +00:00
|
|
|
|
except Exception as e:
|
2021-07-30 13:00:23 +00:00
|
|
|
|
print('ERROR: webfingerHandle ' + str(e))
|
|
|
|
|
return None
|
2020-05-07 13:21:58 +00:00
|
|
|
|
|
|
|
|
|
if result:
|
2021-07-29 22:48:31 +00:00
|
|
|
|
storeWebfingerInCache(wfHandle, result, cachedWebfingers)
|
2020-05-07 13:26:55 +00:00
|
|
|
|
else:
|
2021-03-14 19:22:58 +00:00
|
|
|
|
if debug:
|
|
|
|
|
print("WARN: Unable to webfinger " + url + ' ' +
|
|
|
|
|
'nickname: ' + str(nickname) + ' ' +
|
|
|
|
|
'domain: ' + str(wfDomain) + ' ' +
|
|
|
|
|
'headers: ' + str(hdr) + ' ' +
|
|
|
|
|
'params: ' + str(par))
|
2020-05-07 13:26:55 +00:00
|
|
|
|
|
2019-06-28 18:55:29 +00:00
|
|
|
|
return result
|
|
|
|
|
|
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,
|
2021-07-30 10:51:33 +00:00
|
|
|
|
httpPrefix: str, publicKeyPem: str,
|
|
|
|
|
groupAccount: bool) -> {}:
|
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
|
2021-08-14 11:13:39 +00:00
|
|
|
|
personId = localActorUrl(httpPrefix, personName, domain)
|
2021-11-18 17:14:59 +00:00
|
|
|
|
subjectStr = "acct:" + personName + "@" + originalDomain
|
2020-04-04 14:14:25 +00:00
|
|
|
|
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'
|
|
|
|
|
|
2021-10-28 09:34:11 +00:00
|
|
|
|
personLink = httpPrefix + "://" + domain + "/@" + personName
|
2020-04-04 14:14:25 +00:00
|
|
|
|
account = {
|
2019-06-28 18:55:29 +00:00
|
|
|
|
"aliases": [
|
2021-10-28 09:34:11 +00:00
|
|
|
|
personLink,
|
2019-08-23 20:03:06 +00:00
|
|
|
|
personId
|
2019-06-28 18:55:29 +00:00
|
|
|
|
],
|
|
|
|
|
"links": [
|
2021-10-28 09:33:27 +00:00
|
|
|
|
{
|
2021-10-28 09:41:18 +00:00
|
|
|
|
"href": personLink + "/avatar.png",
|
2021-10-28 09:33:27 +00:00
|
|
|
|
"rel": "http://webfinger.net/rel/avatar",
|
|
|
|
|
"type": "image/png"
|
|
|
|
|
},
|
2021-10-28 09:41:18 +00:00
|
|
|
|
{
|
|
|
|
|
"href": httpPrefix + "://" + domain + "/blog/" + personName,
|
|
|
|
|
"rel": "http://webfinger.net/rel/blog"
|
|
|
|
|
},
|
2019-06-28 18:55:29 +00:00
|
|
|
|
{
|
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-23 20:03:06 +00:00
|
|
|
|
"href": personId,
|
2019-06-28 18:55:29 +00:00
|
|
|
|
"rel": "self",
|
|
|
|
|
"type": "application/activity+json"
|
|
|
|
|
}
|
|
|
|
|
],
|
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
|
|
|
|
"""
|
2021-07-06 12:50:38 +00:00
|
|
|
|
metaStr = \
|
|
|
|
|
"<?xml version=’1.0' encoding=’UTF-8'?>" + \
|
|
|
|
|
"<XRD xmlns=’http://docs.oasis-open.org/ns/xri/xrd-1.0'" + \
|
|
|
|
|
" xmlns:hm=’http://host-meta.net/xrd/1.0'>" + \
|
|
|
|
|
"" + \
|
|
|
|
|
"<hm:Host>" + domainFull + "</hm:Host>" + \
|
|
|
|
|
"" + \
|
|
|
|
|
"<Link rel=’lrdd’" + \
|
|
|
|
|
" template=’" + httpPrefix + "://" + domainFull + \
|
|
|
|
|
"/describe?uri={uri}'>" + \
|
|
|
|
|
" <Title>Resource Descriptor</Title>" + \
|
|
|
|
|
" </Link>" + \
|
|
|
|
|
"</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
|
2021-11-18 17:14:59 +00:00
|
|
|
|
resType = 'acct'
|
|
|
|
|
if 'resource=' + resType + ':' in path:
|
|
|
|
|
handle = path.split('resource=' + resType + ':')[1].strip()
|
|
|
|
|
handle = urllib.parse.unquote(handle)
|
|
|
|
|
if debug:
|
|
|
|
|
print('DEBUG: WEBFINGER handle ' + handle)
|
|
|
|
|
elif 'resource=' + resType + '%3A' in path:
|
|
|
|
|
handle = path.split('resource=' + resType + '%3A')[1]
|
|
|
|
|
handle = urllib.parse.unquote(handle.strip())
|
|
|
|
|
if debug:
|
|
|
|
|
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()
|
2021-10-28 13:27: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
|
2021-08-31 19:04:29 +00:00
|
|
|
|
# instance actor
|
|
|
|
|
if handle.startswith('actor@'):
|
|
|
|
|
handle = handle.replace('actor@', 'inbox@', 1)
|
|
|
|
|
elif handle.startswith('Actor@'):
|
|
|
|
|
handle = handle.replace('Actor@', 'inbox@', 1)
|
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
|
|
|
|
|
|
|
|
|
|
2021-10-28 10:08:41 +00:00
|
|
|
|
def _webfingerUpdateAvatar(wfJson: {}, actorJson: {}) -> bool:
|
|
|
|
|
"""Updates the avatar image link
|
|
|
|
|
"""
|
|
|
|
|
found = False
|
|
|
|
|
avatarUrl = actorJson['icon']['url']
|
|
|
|
|
mediaType = actorJson['icon']['mediaType']
|
|
|
|
|
for link in wfJson['links']:
|
|
|
|
|
if not link.get('rel'):
|
|
|
|
|
continue
|
|
|
|
|
if not link['rel'].endswith('://webfinger.net/rel/avatar'):
|
|
|
|
|
continue
|
|
|
|
|
found = True
|
|
|
|
|
if link['href'] != avatarUrl or link['type'] != mediaType:
|
|
|
|
|
link['href'] = avatarUrl
|
|
|
|
|
link['type'] = mediaType
|
|
|
|
|
return True
|
|
|
|
|
break
|
|
|
|
|
if found:
|
|
|
|
|
return False
|
|
|
|
|
wfJson['links'].append({
|
|
|
|
|
"href": avatarUrl,
|
|
|
|
|
"rel": "http://webfinger.net/rel/avatar",
|
|
|
|
|
"type": mediaType
|
|
|
|
|
})
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
2021-10-28 10:21:28 +00:00
|
|
|
|
def _webfingerAddBlogLink(wfJson: {}, actorJson: {}) -> bool:
|
|
|
|
|
"""Adds a blog link to webfinger if needed
|
|
|
|
|
"""
|
|
|
|
|
found = False
|
|
|
|
|
if '/users/' in actorJson['id']:
|
|
|
|
|
blogUrl = \
|
|
|
|
|
actorJson['id'].split('/users/')[0] + '/blog/' + \
|
|
|
|
|
actorJson['id'].split('/users/')[1]
|
|
|
|
|
else:
|
|
|
|
|
blogUrl = \
|
|
|
|
|
actorJson['id'].split('/@')[0] + '/blog/' + \
|
|
|
|
|
actorJson['id'].split('/@')[1]
|
|
|
|
|
for link in wfJson['links']:
|
|
|
|
|
if not link.get('rel'):
|
|
|
|
|
continue
|
|
|
|
|
if not link['rel'].endswith('://webfinger.net/rel/blog'):
|
|
|
|
|
continue
|
|
|
|
|
found = True
|
|
|
|
|
if link['href'] != blogUrl:
|
|
|
|
|
link['href'] = blogUrl
|
|
|
|
|
return True
|
|
|
|
|
break
|
|
|
|
|
if found:
|
|
|
|
|
return False
|
|
|
|
|
wfJson['links'].append({
|
|
|
|
|
"href": blogUrl,
|
|
|
|
|
"rel": "http://webfinger.net/rel/blog"
|
|
|
|
|
})
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
2020-12-22 18:06:23 +00:00
|
|
|
|
def _webfingerUpdateFromProfile(wfJson: {}, actorJson: {}) -> bool:
|
2020-05-04 13:58:24 +00:00
|
|
|
|
"""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",
|
2021-07-06 12:53:10 +00:00
|
|
|
|
"briar": "briar",
|
|
|
|
|
"cwtch": "cwtch",
|
|
|
|
|
"jami": "jami",
|
2020-05-04 13:58:24 +00:00
|
|
|
|
"tox": "toxId"
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-06 13:11:00 +00:00
|
|
|
|
aliasesNotFound = []
|
2021-07-06 13:17:38 +00:00
|
|
|
|
for name, alias in webfingerPropertyName.items():
|
|
|
|
|
aliasesNotFound.append(alias)
|
2021-07-06 13:11:00 +00:00
|
|
|
|
|
2020-05-04 13:58:24 +00:00
|
|
|
|
for propertyValue in actorJson['attachment']:
|
|
|
|
|
if not propertyValue.get('name'):
|
|
|
|
|
continue
|
|
|
|
|
propertyName = propertyValue['name'].lower()
|
2021-07-06 12:50:38 +00:00
|
|
|
|
found = False
|
2021-07-06 12:58:21 +00:00
|
|
|
|
for name, alias in webfingerPropertyName.items():
|
2021-07-06 12:50:38 +00:00
|
|
|
|
if name == propertyName:
|
2021-07-06 13:17:38 +00:00
|
|
|
|
if alias in aliasesNotFound:
|
|
|
|
|
aliasesNotFound.remove(alias)
|
2021-07-06 12:50:38 +00:00
|
|
|
|
found = True
|
|
|
|
|
break
|
|
|
|
|
if not found:
|
2020-05-04 13:58:24 +00:00
|
|
|
|
continue
|
|
|
|
|
if not propertyValue.get('type'):
|
|
|
|
|
continue
|
|
|
|
|
if not propertyValue.get('value'):
|
|
|
|
|
continue
|
|
|
|
|
if propertyValue['type'] != 'PropertyValue':
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
newValue = propertyValue['value'].strip()
|
2021-07-06 13:02:00 +00:00
|
|
|
|
if '://' in newValue:
|
|
|
|
|
newValue = newValue.split('://')[1]
|
|
|
|
|
|
2020-05-04 13:58:24 +00:00
|
|
|
|
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
|
2021-07-06 13:11:00 +00:00
|
|
|
|
|
|
|
|
|
# remove any aliases which are no longer in the actor profile
|
|
|
|
|
removeAlias = []
|
2021-07-06 13:17:38 +00:00
|
|
|
|
for alias in aliasesNotFound:
|
|
|
|
|
for fullAlias in wfJson['aliases']:
|
|
|
|
|
if fullAlias.startswith(alias + ':'):
|
|
|
|
|
removeAlias.append(fullAlias)
|
|
|
|
|
for fullAlias in removeAlias:
|
|
|
|
|
wfJson['aliases'].remove(fullAlias)
|
2021-07-06 13:11:00 +00:00
|
|
|
|
changed = True
|
|
|
|
|
|
2021-10-28 10:08:41 +00:00
|
|
|
|
if _webfingerUpdateAvatar(wfJson, actorJson):
|
2021-10-28 10:21:28 +00:00
|
|
|
|
changed = True
|
|
|
|
|
|
|
|
|
|
if _webfingerAddBlogLink(wfJson, actorJson):
|
2021-10-28 10:08:41 +00:00
|
|
|
|
changed = True
|
|
|
|
|
|
2020-05-04 13:58:24 +00:00
|
|
|
|
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
|
|
|
|
|
|
2020-12-22 18:06:23 +00:00
|
|
|
|
if _webfingerUpdateFromProfile(wfJson, actorJson):
|
2020-05-04 13:58:24 +00:00
|
|
|
|
if saveJson(wfJson, filename):
|
2021-01-10 12:32:09 +00:00
|
|
|
|
storeWebfingerInCache(handle, wfJson, cachedWebfingers)
|