diff --git a/acceptreject.py b/acceptreject.py index 31fabd38..4da82e72 100644 --- a/acceptreject.py +++ b/acceptreject.py @@ -14,18 +14,22 @@ from utils import urlPermitted from utils import getDomainFromActor from utils import getNicknameFromActor from utils import domainPermitted +from utils import followPerson def createAcceptReject(baseDir: str,federationList: [],capsList: [], \ nickname: str,domain: str,port: int, \ toUrl: str,ccUrl: str,httpPrefix: str, \ - objectUrl: str,acceptType: str) -> {}: + objectJson: {},acceptType: str) -> {}: """Accepts or rejects something (eg. a follow request or offer) Typically toUrl will be https://www.w3.org/ns/activitystreams#Public and ccUrl might be a specific person favorited or repeated and the followers url objectUrl is typically the url of the message, corresponding to url or atomUri in createPostBase """ - if not urlPermitted(objectUrl,federationList,capsList,"inbox:write"): + if not objectJson.get('actor'): + return None + + if not urlPermitted(objectJson['actor'],federationList,capsList,"inbox:write"): return None if port!=80 and port!=443: @@ -36,7 +40,7 @@ def createAcceptReject(baseDir: str,federationList: [],capsList: [], \ 'actor': httpPrefix+'://'+domain+'/users/'+nickname, 'to': [toUrl], 'cc': [], - 'object': objectUrl + 'object': objectJson } if ccUrl: if len(ccUrl)>0: @@ -46,22 +50,82 @@ def createAcceptReject(baseDir: str,federationList: [],capsList: [], \ def createAccept(baseDir: str,federationList: [],capsList: [], \ nickname: str,domain: str,port: int, \ toUrl: str,ccUrl: str,httpPrefix: str, \ - objectUrl: str) -> {}: + objectJson: {}) -> {}: return createAcceptReject(baseDir,federationList,capsList, \ nickname,domain,port, \ toUrl,ccUrl,httpPrefix, \ - objectUrl,'Accept') + objectJson,'Accept') def createReject(baseDir: str,federationList: [],capsList: [], \ nickname: str,domain: str,port: int, \ toUrl: str,ccUrl: str,httpPrefix: str, \ - objectUrl: str) -> {}: + objectJson: {}) -> {}: return createAcceptReject(baseDir,federationList,capsList, \ nickname,domain,port, \ toUrl,ccUrl, \ - httpPrefix,objectUrl,'Reject') + httpPrefix,objectJson,'Reject') -def receiveAcceptReject(session,baseDir: str,httpPrefix: str,port: int, \ +def acceptFollow(baseDir: str,domain : str,messageJson: {}, \ + federationList: [],capsList: [],debug : bool) -> None: + if not messageJson.get('object'): + return + if not messageJson['object'].get('type'): + return + if not messageJson['object'].get('actor'): + return + # no, this isn't a mistake + if not messageJson['object'].get('object'): + return + if not messageJson['object']['type']=='Follow': + return + if debug: + print('DEBUG: follow Accept received') + thisActor=messageJson['object']['actor'] + nickname=getNicknameFromActor(thisActor) + acceptedDomain,acceptedPort=getDomainFromActor(thisActor) + if not acceptedDomain: + if debug: + print('DEBUG: domain not found in '+thisActor) + return + if acceptedDomain != domain: + if debug: + print('DEBUG: domain mismatch '+acceptedDomain+' != '+domain) + return + if not nickname: + if debug: + print('DEBUG: nickname not found in '+thisActor) + return + if acceptedPort: + if not '/'+domain+':'+str(acceptedPort)+'/users/'+nickname in thisActor: + if debug: + print('DEBUG: unrecognized actor '+thisActor) + return + else: + if not '/'+domain+'/users/'+nickname in thisActor: + if debug: + print('DEBUG: unrecognized actor '+thisActor) + return + followedActor=messageJson['object']['object'] + followedDomain,port=getDomainFromActor(followedActor) + if not followedDomain: + return + followedDomainFull=followedDomain + if port: + followedDomainFull=followedDomain+':'+str(port) + followedNickname=getNicknameFromActor(followedActor) + if not followedNickname: + return + if followPerson(baseDir,nickname,domain, \ + followedNickname,followedDomain, \ + federationList,debug): + if debug: + print('DEBUG: '+nickname+'@'+domain+' followed '+followedNickname+'@'+followedDomain) + else: + if debug: + print('DEBUG: Unable to create follow - '+nickname+'@'+domain+' -> '+followedNickname+'@'+followedDomain) + +def receiveAcceptReject(session,baseDir: str, \ + httpPrefix: str,domain :str,port: int, \ sendThreads: [],postLog: [],cachedWebfingers: {}, \ personCache: {},messageJson: {},federationList: [], \ capsList: [],debug : bool) -> bool: @@ -88,10 +152,7 @@ def receiveAcceptReject(session,baseDir: str,httpPrefix: str,port: int, \ print('DEBUG: '+messageJson['type']+' does not contain a nickname') return False handle=nickname.lower()+'@'+domain.lower() - if '/users/' not in messageJson['object']: - if debug: - print('DEBUG: "users" not found within object of '+messageJson['type']) - return False + acceptFollow(baseDir,domain,messageJson,federationList,capsList,debug) if debug: print('DEBUG: Uh, '+messageJson['type']+', I guess') return True diff --git a/epicyon.py b/epicyon.py index 2c40c8a0..cf992593 100644 --- a/epicyon.py +++ b/epicyon.py @@ -34,7 +34,7 @@ from daemon import runDaemon import socket from follow import clearFollows from follow import clearFollowers -from follow import followPerson +from utils import followPerson from follow import followerOfPerson from follow import unfollowPerson from follow import unfollowerOfPerson diff --git a/follow.py b/follow.py index 5ef87918..16d836b0 100644 --- a/follow.py +++ b/follow.py @@ -14,6 +14,8 @@ from person import validNickname from utils import domainPermitted from utils import getDomainFromActor from utils import getNicknameFromActor +from utils import getStatusNumber +from utils import followPerson from posts import sendSignedJson from capabilities import isCapable from acceptreject import createAccept @@ -43,39 +45,14 @@ def getFollowersOfPerson(baseDir: str, \ break return followers -def followPerson(baseDir: str,nickname: str, domain: str, \ - followNickname: str, followDomain: str, \ - federationList: [], followFile='following.txt') -> bool: - """Adds a person to the follow list - """ - if not domainPermitted(followDomain.lower().replace('\n',''), \ - federationList): - return False - handle=nickname.lower()+'@'+domain.lower() - handleToFollow=followNickname.lower()+'@'+followDomain.lower() - if not os.path.isdir(baseDir+'/accounts'): - os.mkdir(baseDir+'/accounts') - if not os.path.isdir(baseDir+'/accounts/'+handle): - os.mkdir(baseDir+'/accounts/'+handle) - filename=baseDir+'/accounts/'+handle+'/'+followFile - if os.path.isfile(filename): - if handleToFollow in open(filename).read(): - return True - with open(filename, "a") as followfile: - followfile.write(handleToFollow+'\n') - return True - with open(filename, "w") as followfile: - followfile.write(handleToFollow+'\n') - return True - def followerOfPerson(baseDir: str,nickname: str, domain: str, \ followerNickname: str, followerDomain: str, \ - federationList: []) -> bool: + federationList: [],debug :bool) -> bool: """Adds a follower of the given person """ - return followPerson(baseDir,nickname, domain, \ - followerNickname, followerDomain, \ - federationList, 'followers.txt') + return followPerson(baseDir,nickname,domain, \ + followerNickname,followerDomain, \ + federationList,debug,'followers.txt') def unfollowPerson(baseDir: str,nickname: str, domain: str, \ followNickname: str, followDomain: str, \ @@ -293,7 +270,7 @@ def receiveFollowRequest(session,baseDir: str,httpPrefix: str, \ baseDir+'/accounts/'+handleToFollow) return False if not followerOfPerson(baseDir,nicknameToFollow,domainToFollow, \ - nickname,domain,federationList): + nickname,domain,federationList,debug): if debug: print('DEBUG: '+nickname+'@'+domain+ \ ' is already a follower of '+ \ @@ -306,7 +283,7 @@ def receiveFollowRequest(session,baseDir: str,httpPrefix: str, \ personUrl=messageJson['actor'] acceptJson=createAccept(baseDir,federationList,capsList, \ nickname,domain,port, \ - personUrl,'',httpPrefix,messageJson['object']) + personUrl,'',httpPrefix,messageJson) if debug: pprint(acceptJson) print('DEBUG: sending follow Accept from '+ \ @@ -346,15 +323,24 @@ def sendFollowRequest(session,baseDir: str, \ if capsList: if not isCapable(followActor,capsList,'inbox:write'): return None - + + statusNumber,published = getStatusNumber() + + followedId=followHttpPrefix+'://'+requestDomain+'/users/'+followNickname + newFollowJson = { + 'id': httpPrefix+'://'+domain+'/users/'+nickname+'/statuses/'+statusNumber, 'type': 'Follow', 'actor': followActor, - 'object': followHttpPrefix+'://'+requestDomain+'/users/'+followNickname + 'object': followedId, + 'to': [followedId], + 'cc': ['https://www.w3.org/ns/activitystreams#Public'], + 'published': published } sendSignedJson(newFollowJson,session,baseDir,nickname,domain,port, \ - followNickname,followDomain,followPort, '', \ + followNickname,followDomain,followPort, \ + 'https://www.w3.org/ns/activitystreams#Public', \ httpPrefix,True,clientToServer, \ federationList, capsList, \ sendThreads,postLog,cachedWebfingers,personCache, debug) diff --git a/inbox.py b/inbox.py index 5351249d..eb605301 100644 --- a/inbox.py +++ b/inbox.py @@ -241,7 +241,7 @@ def runInboxQueue(baseDir: str,httpPrefix: str,sendThreads: [],postLog: [],cache continue if receiveAcceptReject(session, \ - baseDir,httpPrefix,port, \ + baseDir,httpPrefix,domain,port, \ sendThreads,postLog, \ cachedWebfingers, personCache, diff --git a/tests.py b/tests.py index de9e3450..f0e22105 100644 --- a/tests.py +++ b/tests.py @@ -26,7 +26,7 @@ from posts import archivePosts from posts import noOfFollowersOnDomain from follow import clearFollows from follow import clearFollowers -from follow import followPerson +from utils import followPerson from follow import followerOfPerson from follow import unfollowPerson from follow import unfollowerOfPerson @@ -123,8 +123,8 @@ def createServerAlice(path: str,domain: str,port: int,federationList: [],capsLis deleteAllPosts(path,nickname,domain,'inbox') deleteAllPosts(path,nickname,domain,'outbox') if hasFollows: - followPerson(path,nickname,domain,'bob','127.0.0.100:61936',federationList) - followerOfPerson(path,nickname,domain,'bob','127.0.0.100:61936',federationList) + followPerson(path,nickname,domain,'bob','127.0.0.100:61936',federationList,True) + followerOfPerson(path,nickname,domain,'bob','127.0.0.100:61936',federationList,True) if hasPosts: createPublicPost(path,nickname, domain, port,httpPrefix, "No wise fish would go anywhere without a porpoise", False, True, clientToServer,capsList) createPublicPost(path,nickname, domain, port,httpPrefix, "Curiouser and curiouser!", False, True, clientToServer,capsList) @@ -149,8 +149,8 @@ def createServerBob(path: str,domain: str,port: int,federationList: [],capsList: deleteAllPosts(path,nickname,domain,'inbox') deleteAllPosts(path,nickname,domain,'outbox') if hasFollows: - followPerson(path,nickname,domain,'alice','127.0.0.50:61935',federationList) - followerOfPerson(path,nickname,domain,'alice','127.0.0.50:61935',federationList) + followPerson(path,nickname,domain,'alice','127.0.0.50:61935',federationList,True) + followerOfPerson(path,nickname,domain,'alice','127.0.0.50:61935',federationList,True) if hasPosts: createPublicPost(path,nickname, domain, port,httpPrefix, "It's your life, live it your way.", False, True, clientToServer,capsList) createPublicPost(path,nickname, domain, port,httpPrefix, "One of the things I've realised is that I am very simple", False, True, clientToServer,capsList) @@ -312,7 +312,8 @@ def testFollowBetweenServers(): for t in range(10): if os.path.isfile(bobDir+'/accounts/bob@'+bobDomain+'/followers.txt'): - break + if os.path.isfile(aliceDir+'/accounts/alice@'+aliceDomain+'/following.txt'): + break time.sleep(1) # stop the servers @@ -351,13 +352,13 @@ def testFollowersOfPerson(): createPerson(baseDir,'sausagedog',domain,port,httpPrefix,True,password) clearFollows(baseDir,nickname,domain) - followPerson(baseDir,nickname,domain,'maxboardroom',domain,federationList) - followPerson(baseDir,'drokk',domain,'ultrapancake',domain,federationList) + followPerson(baseDir,nickname,domain,'maxboardroom',domain,federationList,True) + followPerson(baseDir,'drokk',domain,'ultrapancake',domain,federationList,True) # deliberate duplication - followPerson(baseDir,'drokk',domain,'ultrapancake',domain,federationList) - followPerson(baseDir,'sausagedog',domain,'ultrapancake',domain,federationList) - followPerson(baseDir,nickname,domain,'ultrapancake',domain,federationList) - followPerson(baseDir,nickname,domain,'someother','randodomain.net',federationList) + followPerson(baseDir,'drokk',domain,'ultrapancake',domain,federationList,True) + followPerson(baseDir,'sausagedog',domain,'ultrapancake',domain,federationList,True) + followPerson(baseDir,nickname,domain,'ultrapancake',domain,federationList,True) + followPerson(baseDir,nickname,domain,'someother','randodomain.net',federationList,True) followList=getFollowersOfPerson(baseDir,'ultrapancake',domain) assert len(followList)==3 @@ -388,16 +389,16 @@ def testNoOfFollowersOnDomain(): createPerson(baseDir,'drokk',otherdomain,port,httpPrefix,True,password) createPerson(baseDir,'sausagedog',otherdomain,port,httpPrefix,True,password) - followPerson(baseDir,'drokk',otherdomain,nickname,domain,federationList) - followPerson(baseDir,'sausagedog',otherdomain,nickname,domain,federationList) - followPerson(baseDir,'maxboardroom',otherdomain,nickname,domain,federationList) + followPerson(baseDir,'drokk',otherdomain,nickname,domain,federationList,True) + followPerson(baseDir,'sausagedog',otherdomain,nickname,domain,federationList,True) + followPerson(baseDir,'maxboardroom',otherdomain,nickname,domain,federationList,True) - followerOfPerson(baseDir,nickname,domain,'cucumber','sandwiches.party',federationList) - followerOfPerson(baseDir,nickname,domain,'captainsensible','damned.zone',federationList) - followerOfPerson(baseDir,nickname,domain,'pilchard','zombies.attack',federationList) - followerOfPerson(baseDir,nickname,domain,'drokk',otherdomain,federationList) - followerOfPerson(baseDir,nickname,domain,'sausagedog',otherdomain,federationList) - followerOfPerson(baseDir,nickname,domain,'maxboardroom',otherdomain,federationList) + followerOfPerson(baseDir,nickname,domain,'cucumber','sandwiches.party',federationList,True) + followerOfPerson(baseDir,nickname,domain,'captainsensible','damned.zone',federationList,True) + followerOfPerson(baseDir,nickname,domain,'pilchard','zombies.attack',federationList,True) + followerOfPerson(baseDir,nickname,domain,'drokk',otherdomain,federationList,True) + followerOfPerson(baseDir,nickname,domain,'sausagedog',otherdomain,federationList,True) + followerOfPerson(baseDir,nickname,domain,'maxboardroom',otherdomain,federationList,True) followersOnOtherDomain=noOfFollowersOnDomain(baseDir,nickname+'@'+domain, otherdomain) assert followersOnOtherDomain==3 @@ -426,11 +427,11 @@ def testFollows(): createPerson(baseDir,nickname,domain,port,httpPrefix,True,password) clearFollows(baseDir,nickname,domain) - followPerson(baseDir,nickname,domain,'badger','wild.com',federationList) - followPerson(baseDir,nickname,domain,'squirrel','secret.com',federationList) - followPerson(baseDir,nickname,domain,'rodent','drainpipe.com',federationList) - followPerson(baseDir,nickname,domain,'batman','mesh.com',federationList) - followPerson(baseDir,nickname,domain,'giraffe','trees.com',federationList) + followPerson(baseDir,nickname,domain,'badger','wild.com',federationList,False) + followPerson(baseDir,nickname,domain,'squirrel','secret.com',federationList,False) + followPerson(baseDir,nickname,domain,'rodent','drainpipe.com',federationList,False) + followPerson(baseDir,nickname,domain,'batman','mesh.com',federationList,False) + followPerson(baseDir,nickname,domain,'giraffe','trees.com',federationList,False) f = open(baseDir+'/accounts/'+nickname+'@'+domain+'/following.txt', "r") domainFound=False @@ -453,11 +454,11 @@ def testFollows(): assert(domainFound==False) clearFollowers(baseDir,nickname,domain) - followerOfPerson(baseDir,nickname,domain,'badger','wild.com',federationList) - followerOfPerson(baseDir,nickname,domain,'squirrel','secret.com',federationList) - followerOfPerson(baseDir,nickname,domain,'rodent','drainpipe.com',federationList) - followerOfPerson(baseDir,nickname,domain,'batman','mesh.com',federationList) - followerOfPerson(baseDir,nickname,domain,'giraffe','trees.com',federationList) + followerOfPerson(baseDir,nickname,domain,'badger','wild.com',federationList,False) + followerOfPerson(baseDir,nickname,domain,'squirrel','secret.com',federationList,False) + followerOfPerson(baseDir,nickname,domain,'rodent','drainpipe.com',federationList,False) + followerOfPerson(baseDir,nickname,domain,'batman','mesh.com',federationList,False) + followerOfPerson(baseDir,nickname,domain,'giraffe','trees.com',federationList,False) f = open(baseDir+'/accounts/'+nickname+'@'+domain+'/followers.txt', "r") for followerDomain in f: diff --git a/utils.py b/utils.py index be2f7061..6506bafa 100644 --- a/utils.py +++ b/utils.py @@ -80,4 +80,30 @@ def getDomainFromActor(actor: str) -> (str,int): domain=domain.split(':')[0] return domain,port - +def followPerson(baseDir: str,nickname: str, domain: str, \ + followNickname: str, followDomain: str, \ + federationList: [],debug: bool, \ + followFile='following.txt') -> bool: + """Adds a person to the follow list + """ + if not domainPermitted(followDomain.lower().replace('\n',''), \ + federationList): + if debug: + print('DEBUG: follow of domain '+followDomain+' not permitted') + return False + handle=nickname.lower()+'@'+domain.lower() + handleToFollow=followNickname.lower()+'@'+followDomain.lower() + if not os.path.isdir(baseDir+'/accounts'): + os.mkdir(baseDir+'/accounts') + if not os.path.isdir(baseDir+'/accounts/'+handle): + os.mkdir(baseDir+'/accounts/'+handle) + filename=baseDir+'/accounts/'+handle+'/'+followFile + if os.path.isfile(filename): + if handleToFollow in open(filename).read(): + return True + with open(filename, "a") as followfile: + followfile.write(handleToFollow+'\n') + return True + with open(filename, "w") as followfile: + followfile.write(handleToFollow+'\n') + return True