main2
Bob Mottram 2019-11-03 15:27:29 +00:00
parent 4e84f2c331
commit ad92d8ee08
6 changed files with 453 additions and 240 deletions

View File

@ -58,9 +58,12 @@ def createAcceptReject(baseDir: str,federationList: [], \
def createAccept(baseDir: str,federationList: [], \ def createAccept(baseDir: str,federationList: [], \
nickname: str,domain: str,port: int, \ nickname: str,domain: str,port: int, \
toUrl: str,ccUrl: str,httpPrefix: str, \ toUrl: str,ccUrl: str,httpPrefix: str, \
objectJson: {},acceptedCaps=["inbox:write","objects:read"]) -> {}: objectJson: {}, \
acceptedCaps=["inbox:write","objects:read"]) -> {}:
# create capabilities accept # create capabilities accept
ocapNew=capabilitiesAccept(baseDir,httpPrefix,nickname,domain,port,toUrl,True,acceptedCaps) ocapNew= \
capabilitiesAccept(baseDir,httpPrefix, \
nickname,domain,port,toUrl,True,acceptedCaps)
return createAcceptReject(baseDir,federationList, \ return createAcceptReject(baseDir,federationList, \
nickname,domain,port, \ nickname,domain,port, \
toUrl,ccUrl,httpPrefix, \ toUrl,ccUrl,httpPrefix, \
@ -94,17 +97,6 @@ def acceptFollow(baseDir: str,domain : str,messageJson: {}, \
if debug: if debug:
print('DEBUG: No "to" parameter in follow Accept') print('DEBUG: No "to" parameter in follow Accept')
return return
#if len(messageJson['object']['to'])!=1:
# if debug:
# print('DEBUG: "to" does not contain a single recipient')
# print(str(messageJson['object']['to']))
# if messageJson['object'].get('object'):
# if not isinstance(messageJson['object']['object'], str):
# messageJson['object']['to']=messageJson['object']['object']
# else:
# return
# else:
# return
if debug: if debug:
print('DEBUG: follow Accept received') print('DEBUG: follow Accept received')
thisActor=messageJson['object']['actor'] thisActor=messageJson['object']['actor']
@ -117,19 +109,16 @@ def acceptFollow(baseDir: str,domain : str,messageJson: {}, \
if debug: if debug:
print('DEBUG: domain not found in '+thisActor) print('DEBUG: domain not found in '+thisActor)
return return
#if acceptedDomain != domain:
# if debug:
# print('DEBUG: domain mismatch '+acceptedDomain+' != '+domain)
# return
if not nickname: if not nickname:
if debug: if debug:
print('DEBUG: nickname not found in '+thisActor) print('DEBUG: nickname not found in '+thisActor)
return return
if acceptedPort: if acceptedPort:
if not '/'+acceptedDomain+':'+str(acceptedPort)+'/users/'+nickname in thisActor: if '/'+acceptedDomain+':'+str(acceptedPort)+'/users/'+nickname not in thisActor:
if debug: if debug:
print('Port: '+str(acceptedPort)) print('Port: '+str(acceptedPort))
print('Expected: /'+acceptedDomain+':'+str(acceptedPort)+'/users/'+nickname) print('Expected: /'+acceptedDomain+':'+str(acceptedPort)+ \
'/users/'+nickname)
print('Actual: '+thisActor) print('Actual: '+thisActor)
print('DEBUG: unrecognized actor '+thisActor) print('DEBUG: unrecognized actor '+thisActor)
return return
@ -167,10 +156,13 @@ def acceptFollow(baseDir: str,domain : str,messageJson: {}, \
followedNickname,followedDomainFull, \ followedNickname,followedDomainFull, \
federationList,debug): federationList,debug):
if debug: if debug:
print('DEBUG: '+nickname+'@'+acceptedDomainFull+' followed '+followedNickname+'@'+followedDomainFull) print('DEBUG: '+nickname+'@'+acceptedDomainFull+ \
' followed '+followedNickname+'@'+followedDomainFull)
else: else:
if debug: if debug:
print('DEBUG: Unable to create follow - '+nickname+'@'+acceptedDomain+' -> '+followedNickname+'@'+followedDomain) print('DEBUG: Unable to create follow - '+ \
nickname+'@'+acceptedDomain+' -> '+ \
followedNickname+'@'+followedDomain)
def receiveAcceptReject(session,baseDir: str, \ def receiveAcceptReject(session,baseDir: str, \
httpPrefix: str,domain :str,port: int, \ httpPrefix: str,domain :str,port: int, \
@ -189,18 +181,21 @@ def receiveAcceptReject(session,baseDir: str, \
'/channel/' not in messageJson['actor'] and \ '/channel/' not in messageJson['actor'] and \
'/profile/' not in messageJson['actor']: '/profile/' not in messageJson['actor']:
if debug: if debug:
print('DEBUG: "users" or "profile" missing from actor in '+messageJson['type']+'. Assuming single user instance.') print('DEBUG: "users" or "profile" missing from actor in '+ \
messageJson['type']+'. Assuming single user instance.')
domain,tempPort=getDomainFromActor(messageJson['actor']) domain,tempPort=getDomainFromActor(messageJson['actor'])
if not domainPermitted(domain,federationList): if not domainPermitted(domain,federationList):
if debug: if debug:
print('DEBUG: '+messageJson['type']+' from domain not permitted - '+domain) print('DEBUG: '+messageJson['type']+ \
' from domain not permitted - '+domain)
return False return False
nickname=getNicknameFromActor(messageJson['actor']) nickname=getNicknameFromActor(messageJson['actor'])
if not nickname: if not nickname:
# single user instance # single user instance
nickname='dev' nickname='dev'
if debug: if debug:
print('DEBUG: '+messageJson['type']+' does not contain a nickname. Assuming single user instance.') print('DEBUG: '+messageJson['type']+ \
' does not contain a nickname. Assuming single user instance.')
handle=nickname.lower()+'@'+domain.lower() handle=nickname.lower()+'@'+domain.lower()
# receive follow accept # receive follow accept
acceptFollow(baseDir,domain,messageJson,federationList,debug) acceptFollow(baseDir,domain,messageJson,federationList,debug)

View File

@ -46,7 +46,8 @@ def outboxAnnounce(baseDir: str,messageJson: {},debug: bool) -> bool:
domain,port=getDomainFromActor(messageJson['actor']) domain,port=getDomainFromActor(messageJson['actor'])
postFilename=locatePost(baseDir,nickname,domain,messageJson['object']) postFilename=locatePost(baseDir,nickname,domain,messageJson['object'])
if postFilename: if postFilename:
updateAnnounceCollection(baseDir,postFilename,messageJson['actor'],domain,debug) updateAnnounceCollection(baseDir,postFilename, \
messageJson['actor'],domain,debug)
return True return True
if messageJson['type']=='Undo': if messageJson['type']=='Undo':
if not isinstance(messageJson['object'], dict): if not isinstance(messageJson['object'], dict):
@ -61,22 +62,29 @@ def outboxAnnounce(baseDir: str,messageJson: {},debug: bool) -> bool:
print('WARN: no nickname found in '+messageJson['actor']) print('WARN: no nickname found in '+messageJson['actor'])
return False return False
domain,port=getDomainFromActor(messageJson['actor']) domain,port=getDomainFromActor(messageJson['actor'])
postFilename=locatePost(baseDir,nickname,domain,messageJson['object']['object']) postFilename= \
locatePost(baseDir,nickname,domain, \
messageJson['object']['object'])
if postFilename: if postFilename:
undoAnnounceCollectionEntry(baseDir,postFilename,messageJson['actor'],domain,debug) undoAnnounceCollectionEntry(baseDir,postFilename, \
messageJson['actor'], \
domain,debug)
return True return True
return False return False
def undoAnnounceCollectionEntry(baseDir: str,postFilename: str,actor: str,domain: str,debug: bool) -> None: def undoAnnounceCollectionEntry(baseDir: str,postFilename: str, \
"""Undoes an announce for a particular actor by removing it from the "shares" actor: str,domain: str,debug: bool) -> None:
collection within a post. Note that the "shares" collection has no relation """Undoes an announce for a particular actor by removing it from
to shared items in shares.py. It's shares of posts, not shares of physical objects. the "shares" collection within a post. Note that the "shares"
collection has no relation to shared items in shares.py. It's
shares of posts, not shares of physical objects.
""" """
postJsonObject=loadJson(postFilename) postJsonObject=loadJson(postFilename)
if postJsonObject: if postJsonObject:
# remove any cached version of this announce so that the like icon is changed # remove any cached version of this announce so that the like icon is changed
nickname=getNicknameFromActor(actor) nickname=getNicknameFromActor(actor)
cachedPostFilename=getCachedPostFilename(baseDir,nickname,domain,postJsonObject) cachedPostFilename= \
getCachedPostFilename(baseDir,nickname,domain,postJsonObject)
if os.path.isfile(cachedPostFilename): if os.path.isfile(cachedPostFilename):
os.remove(cachedPostFilename) os.remove(cachedPostFilename)
@ -113,19 +121,24 @@ def undoAnnounceCollectionEntry(baseDir: str,postFilename: str,actor: str,domain
print('DEBUG: shares (announcements) was removed from post') print('DEBUG: shares (announcements) was removed from post')
del postJsonObject['object']['shares'] del postJsonObject['object']['shares']
else: else:
postJsonObject['object']['shares']['totalItems']=len(postJsonObject['object']['shares']['items']) postJsonObject['object']['shares']['totalItems']= \
len(postJsonObject['object']['shares']['items'])
saveJson(postJsonObject,postFilename) saveJson(postJsonObject,postFilename)
def updateAnnounceCollection(baseDir: str,postFilename: str,actor: str,domain: str,debug: bool) -> None: def updateAnnounceCollection(baseDir: str,postFilename: str, \
actor: str,domain: str,debug: bool) -> None:
"""Updates the announcements collection within a post """Updates the announcements collection within a post
Confusingly this is known as "shares", but isn't the same as shared items within shares.py Confusingly this is known as "shares", but isn't the
same as shared items within shares.py
It's shares of posts, not shares of physical objects. It's shares of posts, not shares of physical objects.
""" """
postJsonObject=loadJson(postFilename) postJsonObject=loadJson(postFilename)
if postJsonObject: if postJsonObject:
# remove any cached version of this announce so that the like icon is changed # remove any cached version of this announce so that the like
# icon is changed
nickname=getNicknameFromActor(actor) nickname=getNicknameFromActor(actor)
cachedPostFilename=getCachedPostFilename(baseDir,nickname,domain,postJsonObject) cachedPostFilename= \
getCachedPostFilename(baseDir,nickname,domain,postJsonObject)
if os.path.isfile(cachedPostFilename): if os.path.isfile(cachedPostFilename):
os.remove(cachedPostFilename) os.remove(cachedPostFilename)
@ -139,7 +152,8 @@ def updateAnnounceCollection(baseDir: str,postFilename: str,actor: str,domain: s
postUrl=postJsonObject['id'].replace('/activity','')+'/shares' postUrl=postJsonObject['id'].replace('/activity','')+'/shares'
if not postJsonObject['object'].get('shares'): if not postJsonObject['object'].get('shares'):
if debug: if debug:
print('DEBUG: Adding initial shares (announcements) to '+postUrl) print('DEBUG: Adding initial shares (announcements) to '+ \
postUrl)
announcementsJson = { announcementsJson = {
"@context": "https://www.w3.org/ns/activitystreams", "@context": "https://www.w3.org/ns/activitystreams",
'id': postUrl, 'id': postUrl,
@ -162,7 +176,8 @@ def updateAnnounceCollection(baseDir: str,postFilename: str,actor: str,domain: s
'actor': actor 'actor': actor
} }
postJsonObject['object']['shares']['items'].append(newAnnounce) postJsonObject['object']['shares']['items'].append(newAnnounce)
postJsonObject['object']['shares']['totalItems']=len(postJsonObject['object']['shares']['items']) postJsonObject['object']['shares']['totalItems']= \
len(postJsonObject['object']['shares']['items'])
else: else:
if debug: if debug:
print('DEBUG: shares (announcements) section of post has no items list') print('DEBUG: shares (announcements) section of post has no items list')
@ -442,7 +457,8 @@ def sendAnnounceViaServer(baseDir: str,session, \
statusNumber,published = getStatusNumber() statusNumber,published = getStatusNumber()
newAnnounceId= \ newAnnounceId= \
httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname+'/statuses/'+statusNumber httpPrefix+'://'+fromDomainFull+'/users/'+ \
fromNickname+'/statuses/'+statusNumber
newAnnounceJson = { newAnnounceJson = {
"@context": "https://www.w3.org/ns/activitystreams", "@context": "https://www.w3.org/ns/activitystreams",
'actor': httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname, 'actor': httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname,
@ -489,10 +505,6 @@ def sendAnnounceViaServer(baseDir: str,session, \
'Authorization': authHeader} 'Authorization': authHeader}
postResult = \ postResult = \
postJson(session,newAnnounceJson,[],inboxUrl,headers,"inbox:write") postJson(session,newAnnounceJson,[],inboxUrl,headers,"inbox:write")
#if not postResult:
# if debug:
# print('DEBUG: POST announce failed for c2s to '+inboxUrl)
# return 5
if debug: if debug:
print('DEBUG: c2s POST announce success') print('DEBUG: c2s POST announce success')

View File

@ -17,7 +17,9 @@ def hashPassword(password: str) -> str:
"""Hash a password for storing """Hash a password for storing
""" """
salt = hashlib.sha256(os.urandom(60)).hexdigest().encode('ascii') salt = hashlib.sha256(os.urandom(60)).hexdigest().encode('ascii')
pwdhash = hashlib.pbkdf2_hmac('sha512', password.encode('utf-8'), salt, 100000) pwdhash = hashlib.pbkdf2_hmac('sha512', \
password.encode('utf-8'), \
salt, 100000)
pwdhash = binascii.hexlify(pwdhash) pwdhash = binascii.hexlify(pwdhash)
return (salt + pwdhash).decode('ascii') return (salt + pwdhash).decode('ascii')

View File

@ -48,7 +48,8 @@ def getAvailability(baseDir: str,nickname: str,domain: str) -> str:
return actorJson['availability'] return actorJson['availability']
return None return None
def outboxAvailability(baseDir: str,nickname: str,messageJson: {},debug: bool) -> bool: def outboxAvailability(baseDir: str,nickname: str,messageJson: {}, \
debug: bool) -> bool:
"""Handles receiving an availability update """Handles receiving an availability update
""" """
if not messageJson.get('type'): if not messageJson.get('type'):
@ -133,10 +134,6 @@ def sendAvailabilityViaServer(baseDir: str,session, \
'Authorization': authHeader} 'Authorization': authHeader}
postResult = \ postResult = \
postJson(session,newAvailabilityJson,[],inboxUrl,headers,"inbox:write") postJson(session,newAvailabilityJson,[],inboxUrl,headers,"inbox:write")
#if not postResult:
# if debug:
# print('DEBUG: POST announce failed for c2s to '+inboxUrl)
# return 5
if debug: if debug:
print('DEBUG: c2s POST availability success') print('DEBUG: c2s POST availability success')

View File

@ -137,11 +137,13 @@ def isBlocked(baseDir: str,nickname: str,domain: str, \
blockHandle=blockNickname+'@'+blockDomain blockHandle=blockNickname+'@'+blockDomain
if blockHandle in open(globalBlockingFilename).read(): if blockHandle in open(globalBlockingFilename).read():
return True return True
allowFilename=baseDir+'/accounts/'+nickname+'@'+domain+'/allowedinstances.txt' allowFilename= \
baseDir+'/accounts/'+nickname+'@'+domain+'/allowedinstances.txt'
if os.path.isfile(allowFilename): if os.path.isfile(allowFilename):
if blockDomain not in open(allowFilename).read(): if blockDomain not in open(allowFilename).read():
return True return True
blockingFilename=baseDir+'/accounts/'+nickname+'@'+domain+'/blocking.txt' blockingFilename= \
baseDir+'/accounts/'+nickname+'@'+domain+'/blocking.txt'
if os.path.isfile(blockingFilename): if os.path.isfile(blockingFilename):
if '*@'+blockDomain in open(blockingFilename).read(): if '*@'+blockDomain in open(blockingFilename).read():
return True return True
@ -169,7 +171,8 @@ def sendBlockViaServer(baseDir: str,session, \
fromDomainFull=fromDomain+':'+str(fromPort) fromDomainFull=fromDomain+':'+str(fromPort)
toUrl= 'https://www.w3.org/ns/activitystreams#Public' toUrl= 'https://www.w3.org/ns/activitystreams#Public'
ccUrl = httpPrefix + '://'+fromDomainFull+'/users/'+fromNickname+'/followers' ccUrl= \
httpPrefix + '://'+fromDomainFull+'/users/'+fromNickname+'/followers'
blockActor=httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname blockActor=httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname
newBlockJson = { newBlockJson = {
@ -215,10 +218,6 @@ def sendBlockViaServer(baseDir: str,session, \
'Authorization': authHeader} 'Authorization': authHeader}
postResult = \ postResult = \
postJson(session,newBlockJson,[],inboxUrl,headers,"inbox:write") postJson(session,newBlockJson,[],inboxUrl,headers,"inbox:write")
#if not postResult:
# if debug:
# print('DEBUG: POST announce failed for c2s to '+inboxUrl)
# return 5
if debug: if debug:
print('DEBUG: c2s POST block success') print('DEBUG: c2s POST block success')
@ -244,7 +243,8 @@ def sendUndoBlockViaServer(baseDir: str,session, \
fromDomainFull=fromDomain+':'+str(fromPort) fromDomainFull=fromDomain+':'+str(fromPort)
toUrl= 'https://www.w3.org/ns/activitystreams#Public' toUrl= 'https://www.w3.org/ns/activitystreams#Public'
ccUrl = httpPrefix + '://'+fromDomainFull+'/users/'+fromNickname+'/followers' ccUrl= \
httpPrefix + '://'+fromDomainFull+'/users/'+fromNickname+'/followers'
blockActor=httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname blockActor=httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname
newBlockJson = { newBlockJson = {
@ -263,7 +263,8 @@ def sendUndoBlockViaServer(baseDir: str,session, \
handle=httpPrefix+'://'+fromDomainFull+'/@'+fromNickname handle=httpPrefix+'://'+fromDomainFull+'/@'+fromNickname
# lookup the inbox for the To handle # lookup the inbox for the To handle
wfRequest = webfingerHandle(session,handle,httpPrefix,cachedWebfingers, \ wfRequest= \
webfingerHandle(session,handle,httpPrefix,cachedWebfingers, \
fromDomain,projectVersion) fromDomain,projectVersion)
if not wfRequest: if not wfRequest:
if debug: if debug:
@ -294,10 +295,6 @@ def sendUndoBlockViaServer(baseDir: str,session, \
'Authorization': authHeader} 'Authorization': authHeader}
postResult = \ postResult = \
postJson(session,newBlockJson,[],inboxUrl,headers,"inbox:write") postJson(session,newBlockJson,[],inboxUrl,headers,"inbox:write")
#if not postResult:
# if debug:
# print('DEBUG: POST announce failed for c2s to '+inboxUrl)
# return 5
if debug: if debug:
print('DEBUG: c2s POST block success') print('DEBUG: c2s POST block success')

544
daemon.py

File diff suppressed because it is too large Load Diff