From c301f45b33a62d7f2eaac56a3edfb8780f23e0b1 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Thu, 11 Jul 2019 13:29:31 +0100 Subject: [PATCH] Move shared inbox account into daemon --- daemon.py | 5 ++ epicyon.py | 10 +-- follow.py | 38 ++++++++++-- inbox.py | 80 +++++++++++++++++------- like.py | 174 ++++++++++++++++++++++++++++++++--------------------- person.py | 8 +++ tests.py | 69 +++++++++++++++++---- utils.py | 27 +++++++++ 8 files changed, 294 insertions(+), 117 deletions(-) diff --git a/daemon.py b/daemon.py index db53ee92..eb445a60 100644 --- a/daemon.py +++ b/daemon.py @@ -18,6 +18,7 @@ from webfinger import webfingerLookup from webfinger import webfingerHandle from person import personLookup from person import personBoxJson +from person import createSharedInbox from posts import outboxMessageCreateWrap from posts import savePostToBox from inbox import inboxPermittedMessage @@ -540,6 +541,10 @@ def runDaemon(baseDir: str,domain: str,port=80,httpPrefix='https', \ httpd.acceptedCaps.append('inbox:noannounce') if cw: httpd.acceptedCaps.append('inbox:cw') + + print('Creating shared inbox: inbox@'+domain) + createSharedInbox(baseDir,'inbox',domain,port,httpPrefix) + print('Running ActivityPub daemon on ' + domain + ' port ' + str(port)) httpd.thrInboxQueue= \ threadWithTrace(target=runInboxQueue, \ diff --git a/epicyon.py b/epicyon.py index fe038dac..987ef53b 100644 --- a/epicyon.py +++ b/epicyon.py @@ -400,13 +400,9 @@ if not os.path.isdir(baseDir+'/accounts/'+nickname+'@'+domain): setConfigParam(baseDir,'adminPassword',adminPassword) createPerson(baseDir,nickname,domain,port,httpPrefix,True,adminPassword) -if not os.path.isdir(baseDir+'/accounts/inbox@'+domain): - print('Creating shared inbox: inbox@'+domain) - createSharedInbox(baseDir,'inbox',domain,port,httpPrefix) - -if not os.path.isdir(baseDir+'/accounts/capabilities@'+domain): - print('Creating capabilities account which can sign requests') - createCapabilitiesInbox(baseDir,'capabilities',domain,port,httpPrefix) +#if not os.path.isdir(baseDir+'/accounts/capabilities@'+domain): +# print('Creating capabilities account which can sign requests') +# createCapabilitiesInbox(baseDir,'capabilities',domain,port,httpPrefix) if args.testdata: nickname='testuser567' diff --git a/follow.py b/follow.py index 827162b0..e990ddd7 100644 --- a/follow.py +++ b/follow.py @@ -27,6 +27,8 @@ def getFollowersOfPerson(baseDir: str, \ Used by the shared inbox to know who to send incoming mail to """ followers=[] + if ':' in domain: + domain=domain.split(':')[0] handle=nickname.lower()+'@'+domain.lower() if not os.path.isdir(baseDir+'/accounts/'+handle): return followers @@ -345,38 +347,64 @@ def sendFollowRequest(session,baseDir: str, \ return newFollowJson -def getFollowersOfActor(baseDir :str,actor :str,recipientsDict: {}) -> {}: +def getFollowersOfActor(baseDir :str,actor :str,debug: bool) -> {}: """In a shared inbox if we receive a post we know who it's from and if it's addressed to followers then we need to get a list of those. This returns a list of account handles which follow the given actor and also the corresponding capability id if it exists """ + if debug: + print('DEBUG: getting followers of '+actor) + recipientsDict={} if ':' not in actor: return recipientsDict httpPrefix=actor.split(':')[0] nickname=getNicknameFromActor(actor) if not nickname: + if debug: + print('DEBUG: no nickname found in '+actor) return recipientsDict domain,port=getDomainFromActor(actor) if not domain: + if debug: + print('DEBUG: no domain found in '+actor) return recipientsDict actorHandle=nickname+'@'+domain + if debug: + print('DEBUG: searching for handle '+actorHandle) # for each of the accounts for subdir, dirs, files in os.walk(baseDir+'/accounts'): for account in dirs: if '@' in account and not account.startswith('inbox@'): followingFilename = os.path.join(subdir, account)+'/following.txt' + if debug: + print('DEBUG: examining follows of '+account) + print(followingFilename) if os.path.isfile(followingFilename): # does this account follow the given actor? + if debug: + print('DEBUG: checking if '+actorHandle+' in '+followingFilename) if actorHandle in open(followingFilename).read(): + if debug: + print('DEBUG: '+account+' follows '+actorHandle) ocapFilename=baseDir+'/accounts/'+account+'/ocap/accept/'+httpPrefix+':##'+domain+':'+str(port)+'#users#'+nickname+'.json' + if debug: + print('DEBUG: checking capabilities of'+account) if os.path.isfile(ocapFilename): with open(ocapFilename, 'r') as fp: ocapJson=commentjson.load(fp) - if ocapJson.get('id'): - recipientsDict[account]=ocapJson['id'] - else: - recipientsDict[account]=None + if ocapJson.get('id'): + if debug: + print('DEBUG: capabilities id found for '+account) + + recipientsDict[account]=ocapJson['id'] + else: + if debug: + print('DEBUG: capabilities has no id attribute') + recipientsDict[account]=None else: + if debug: + print('DEBUG: No capabilities file found for '+account+' granted by '+actorHandle) + print(ocapFilename) recipientsDict[account]=None return recipientsDict diff --git a/inbox.py b/inbox.py index c071f4a4..d1cb8a96 100644 --- a/inbox.py +++ b/inbox.py @@ -19,6 +19,7 @@ from utils import getStatusNumber from utils import getDomainFromActor from utils import getNicknameFromActor from utils import domainPermitted +from utils import locatePost from httpsig import verifyPostHeaders from session import createSession from session import getJson @@ -87,7 +88,7 @@ def inboxPermittedMessage(domain: str,messageJson: {},federationList: []) -> boo if not urlPermitted(actor,federationList,"inbox:write"): return False - if messageJson['type']!='Follow': + if messageJson['type']!='Follow' and messageJson['type']!='Like': if messageJson.get('object'): if messageJson['object'].get('inReplyTo'): inReplyTo=messageJson['object']['inReplyTo'] @@ -210,7 +211,7 @@ def inboxCheckCapabilities(baseDir :str,nickname :str,domain :str, \ def inboxPostRecipientsAdd(baseDir :str,httpPrefix :str,toList :[], \ recipientsDict :{}, \ domainMatch: str,domain :str, \ - actor :str) -> bool: + actor :str,debug: bool) -> bool: """Given a list of post recipients (toList) from 'to' or 'cc' parameters populate a recipientsDict with the handle and capabilities id for each """ @@ -235,12 +236,23 @@ def inboxPostRecipientsAdd(baseDir :str,httpPrefix :str,toList :[], \ else: recipientsDict[handle]=None else: + if debug: + print('DEBUG: '+ocapFilename+' not found') recipientsDict[handle]=None + else: + if debug: + print('DEBUG: '+baseDir+'/accounts/'+handle+' does not exist') + else: + if debug: + print('DEBUG: '+recipient+' is not local to '+domainMatch) + print(str(toList)) if recipient.endswith('followers'): + if debug: + print('DEBUG: followers detected as post recipients') followerRecipients=True return followerRecipients,recipientsDict -def inboxPostRecipients(baseDir :str,postJsonObject :{},httpPrefix :str,domain : str,port :int) -> ([],[]): +def inboxPostRecipients(baseDir :str,postJsonObject :{},httpPrefix :str,domain : str,port :int, debug :bool) -> ([],[]): """Returns dictionaries containing the recipients of the given post The shared dictionary contains followers """ @@ -248,6 +260,9 @@ def inboxPostRecipients(baseDir :str,postJsonObject :{},httpPrefix :str,domain : recipientsDictFollowers={} if not postJsonObject.get('actor'): + if debug: + pprint(postJsonObject) + print('WARNING: inbox post has no actor') return recipientsDict,recipientsDictFollowers if ':' in domain: @@ -264,29 +279,48 @@ def inboxPostRecipients(baseDir :str,postJsonObject :{},httpPrefix :str,domain : if postJsonObject.get('object'): if isinstance(postJsonObject['object'], dict): if postJsonObject['object'].get('to'): + if debug: + print('DEBUG: resolving "to"') includesFollowers,recipientsDict= \ inboxPostRecipientsAdd(baseDir,httpPrefix, \ postJsonObject['object']['to'], \ recipientsDict, \ - domainMatch,domainBase,actor) + domainMatch,domainBase, \ + actor,debug) if includesFollowers: followerRecipients=True + else: + if debug: + print('DEBUG: inbox post has no "to"') if postJsonObject['object'].get('cc'): includesFollowers,recipientsDict= \ inboxPostRecipientsAdd(baseDir,httpPrefix, \ postJsonObject['object']['cc'], \ recipientsDict, \ - domainMatch,domainBase,actor) + domainMatch,domainBase, \ + actor,debug) if includesFollowers: followerRecipients=True + else: + if debug: + print('DEBUG: inbox post has no cc') + else: + if debug: + if isinstance(postJsonObject['object'], str): + if '/statuses/' in postJsonObject['object']: + print('DEBUG: inbox item is a link to a post') + else: + if '/users/' in postJsonObject['object']: + print('DEBUG: inbox item is a link to an actor') if postJsonObject.get('to'): includesFollowers,recipientsDict= \ inboxPostRecipientsAdd(baseDir,httpPrefix, \ postJsonObject['to'], \ recipientsDict, \ - domainMatch,domainBase,actor) + domainMatch,domainBase, \ + actor,debug) if includesFollowers: followerRecipients=True @@ -295,16 +329,19 @@ def inboxPostRecipients(baseDir :str,postJsonObject :{},httpPrefix :str,domain : inboxPostRecipientsAdd(baseDir,httpPrefix, \ postJsonObject['cc'], \ recipientsDict, \ - domainMatch,domainBase,actor) + domainMatch,domainBase, \ + actor,debug) if includesFollowers: followerRecipients=True if not followerRecipients: + if debug: + print('DEBUG: no followers were resolved') return recipientsDict,recipientsDictFollowers # now resolve the followers recipientsDictFollowers= \ - getFollowersOfActor(baseDir,actor,recipientsDict) + getFollowersOfActor(baseDir,actor,debug) return recipientsDict,recipientsDictFollowers @@ -388,18 +425,7 @@ def receiveLike(session,handle: str,baseDir: str, \ if not os.path.isdir(baseDir+'/accounts/'+handle): print('DEBUG: unknown recipient of like - '+handle) # if this post in the outbox of the person? - boxName='outbox' - postFilename=baseDir+'/accounts/'+handle+'/'+boxName+'/'+messageJson['object'].replace('/','#')+'.json' - if not os.path.isfile(postFilename): - # if this post in the inbox of the person? - boxName='inbox' - postFilename=baseDir+'/accounts/'+handle+'/'+boxName+'/'+messageJson['object'].replace('/','#')+'.json' - if not os.path.isfile(postFilename): - # if this post in the shared inbox? - handle='inbox@'+domain - postFilename=baseDir+'/accounts/'+handle+'/'+boxName+'/'+messageJson['object'].replace('/','#')+'.json' - if not os.path.isfile(postFilename): - postFilename=None + postFilename=locatePost(baseDir,handle.split('@')[0],handle.split('@')[1],messageJson['object']) if not postFilename: if debug: print('DEBUG: post not found in inbox or outbox') @@ -569,7 +595,15 @@ def runInboxQueue(baseDir: str,httpPrefix: str,sendThreads: [],postLog: [],cache # get recipients list recipientsDict,recipientsDictFollowers= \ - inboxPostRecipients(baseDir,queueJson['post'],httpPrefix,domain,port) + inboxPostRecipients(baseDir,queueJson['post'],httpPrefix,domain,port,debug) + if len(recipientsDict.items())==0 and \ + len(recipientsDictFollowers.items())==0: + if debug: + pprint(queueJson['post']) + print('DEBUG: no recipients were resolved for post arriving in inbox') + os.remove(queueFilename) + queue.pop(0) + continue # if there are only a small number of followers then process them as if they # were specifically addresses to particular accounts @@ -579,7 +613,7 @@ def runInboxQueue(baseDir: str,httpPrefix: str,sendThreads: [],postLog: [],cache if debug: print('DEBUG: moving '+str(noOfFollowItems)+' inbox posts addressed to followers') for handle,postItem in recipientsDictFollowers.items(): - recipientsDict['handle']=postItem + recipientsDict[handle]=postItem recipientsDictFollowers={} recipientsList=[recipientsDict,recipientsDictFollowers] @@ -587,7 +621,7 @@ def runInboxQueue(baseDir: str,httpPrefix: str,sendThreads: [],postLog: [],cache print('*************************************') print('Resolved recipients list:') pprint(recipientsDict) - print('Resolved sollowers list:') + print('Resolved followers list:') pprint(recipientsDictFollowers) print('*************************************') diff --git a/like.py b/like.py index 21452ec0..f1e4b721 100644 --- a/like.py +++ b/like.py @@ -8,80 +8,28 @@ __status__ = "Production" import json import commentjson +from pprint import pprint from utils import urlPermitted from utils import getNicknameFromActor from utils import getDomainFromActor +from utils import locatePost +from posts import sendSignedJson -def like(session,baseDir: str,federationList: [],nickname: str,domain: str,port: int, \ - ccUrl: str,httpPrefix: str,objectUrl: str,clientToServer: bool, \ - sendThreads: [],postLog: [],personCache: {},cachedWebfingers: {}) -> {}: - """Creates a like - ccUrl might be a specific person whose post was liked - objectUrl is typically the url of the message, corresponding to url or atomUri in createPostBase - """ - if not urlPermitted(objectUrl,federationList,"inbox:write"): - return None - - if port!=80 and port!=443: - domain=domain+':'+str(port) - - newLikeJson = { - 'type': 'Like', - 'actor': httpPrefix+'://'+domain+'/users/'+nickname, - 'object': objectUrl, - 'to': [httpPrefix+'://'+domain+'/users/'+nickname+'/followers'], - 'cc': [] - } - if ccUrl: - if len(ccUrl)>0: - newLikeJson['cc']=ccUrl - - # Extract the domain and nickname from a statuses link - likedPostNickname=None - likedPostDomain=None - likedPostPort=None - if '/users/' in objectUrl: - likedPostNickname=getNicknameFromActor(objectUrl) - likedPostDomain,likedPostPort=getDomainFromActor(objectUrl) - - if likedPostNickname: - sendSignedJson(newlikeJson,session,baseDir, \ - nickname,domain,port, \ - likedPostNickname,likedPostDomain,likedPostPort, \ - 'https://www.w3.org/ns/activitystreams#Public', \ - httpPrefix,True,clientToServer,federationList, \ - sendThreads,postLog,cachedWebfingers,personCache,debug) - - return newLikeJson - -def likePost(session,baseDir: str,federationList: [], \ - nickname: str, domain: str, port: int, httpPrefix: str, \ - likeNickname: str, likeDomain: str, likePort: int, \ - likeHttps: bool, likeStatusNumber: int, \ - clientToServer: bool,sendThreads: [],postLog: [], \ - personCache: {},cachedWebfingers: {}) -> {}: - """Likes a given status post - """ - likeDomain=likeDomain - if likePort!=80 and likePort!=443: - likeDomain=likeDomain+':'+str(likePort) - - objectUrl = \ - httpPrefix + '://'+likeDomain+'/users/'+likeNickname+ \ - '/statuses/'+str(likeStatusNumber) - - return like(session,baseDir,federationList,nickname,domain,port, \ - ccUrl,httpPrefix,objectUrl,clientToServer, \ - sendThreads,postLog,personCache,cachedWebfingers) - -def updateLikesCollection(postFilename: str,objectUrl: str, actor: str) -> None: +def updateLikesCollection(postFilename: str,objectUrl: str, actor: str,debug: bool) -> None: """Updates the likes collection within a post """ with open(postFilename, 'r') as fp: postJson=commentjson.load(fp) + if not postJson.get('object'): + if debug: + pprint(postJson) + print('DEBUG: post '+objectUrl+' has no object') + return if not objectUrl.endswith('/likes'): objectUrl=objectUrl+'/likes' - if not postJson.get('likes'): + if not postJson['object'].get('likes'): + if debug: + print('DEBUG: Adding initial likes to '+objectUrl) likesJson = { 'id': objectUrl, 'type': 'Collection', @@ -92,17 +40,103 @@ def updateLikesCollection(postFilename: str,objectUrl: str, actor: str) -> None: }] } - postJson['likes']=likesJson + postJson['object']['likes']=likesJson else: - if postJson['likes'].get('items'): + if postJson['object']['likes'].get('items'): for likeItem in postJson['likes']['items']: - if likeItem['actor']==actor: - return + if likeItem.get('actor'): + if likeItem['actor']==actor: + return newLike={ 'type': 'Like', 'actor': actor } - postJson['likes']['items'].append(newLike) - postJson['likes']['totalItems']=len(postJson['likes']['items']) + postJson['object']['likes']['items'].append(newLike) + postJson['object']['likes']['totalItems']=len(postJson['likes']['items']) + else: + if debug: + print('DEBUG: likes section of post has no items list') + + if debug: + print('DEBUG: saving post with likes added') with open(postFilename, 'w') as fp: commentjson.dump(postJson, fp, indent=4, sort_keys=True) + +def like(session,baseDir: str,federationList: [],nickname: str,domain: str,port: int, \ + ccList: [],httpPrefix: str,objectUrl: str,clientToServer: bool, \ + sendThreads: [],postLog: [],personCache: {},cachedWebfingers: {}, \ + debug: bool) -> {}: + """Creates a like + ccUrl might be a specific person whose post was liked + objectUrl is typically the url of the message, corresponding to url or atomUri in createPostBase + """ + if not urlPermitted(objectUrl,federationList,"inbox:write"): + return None + + fullDomain=domain + if port!=80 and port!=443: + if ':' not in domain: + fullDomain=domain+':'+str(port) + + newLikeJson = { + 'type': 'Like', + 'actor': httpPrefix+'://'+fullDomain+'/users/'+nickname, + 'object': objectUrl, + 'to': [httpPrefix+'://'+fullDomain+'/users/'+nickname+'/followers'], + 'cc': [] + } + if ccList: + if len(ccList)>0: + newLikeJson['cc']=ccList + + # Extract the domain and nickname from a statuses link + likedPostNickname=None + likedPostDomain=None + likedPostPort=None + if '/users/' in objectUrl: + likedPostNickname=getNicknameFromActor(objectUrl) + likedPostDomain,likedPostPort=getDomainFromActor(objectUrl) + + if likedPostNickname: + postFilename=locatePost(baseDir,nickname,domain,objectUrl) + if not postFilename: + return None + + updateLikesCollection(postFilename,objectUrl,newLikeJson['actor'],debug) + + sendSignedJson(newLikeJson,session,baseDir, \ + nickname,domain,port, \ + likedPostNickname,likedPostDomain,likedPostPort, \ + 'https://www.w3.org/ns/activitystreams#Public', \ + httpPrefix,True,clientToServer,federationList, \ + sendThreads,postLog,cachedWebfingers,personCache,debug) + + return newLikeJson + +def likePost(session,baseDir: str,federationList: [], \ + nickname: str,domain: str,port: int,httpPrefix: str, \ + likeNickname: str,likeDomain: str,likePort: int, \ + ccList: [], \ + likeStatusNumber: int,clientToServer: bool, \ + sendThreads: [],postLog: [], \ + personCache: {},cachedWebfingers: {}, \ + debug: bool) -> {}: + """Likes a given status post + """ + likeDomain=likeDomain + if likePort!=80 and likePort!=443: + likeDomain=likeDomain+':'+str(likePort) + + objectUrl = \ + httpPrefix + '://'+likeDomain+'/users/'+likeNickname+ \ + '/statuses/'+str(likeStatusNumber) + + if likePort!=80 and likePort!=443: + ccUrl=httpPrefix+'://'+likeDomain+':'+str(likePort)+'/users/'+likeNickname + else: + ccUrl=httpPrefix+'://'+likeDomain+'/users/'+likeNickname + + return like(session,baseDir,federationList,nickname,domain,port, \ + ccList,httpPrefix,objectUrl,clientToServer, \ + sendThreads,postLog,personCache,cachedWebfingers,debug) + diff --git a/person.py b/person.py index a23d7cff..c7bd7b6e 100644 --- a/person.py +++ b/person.py @@ -88,6 +88,14 @@ def createPersonBase(baseDir: str,nickname: str,domain: str,port: int, \ os.mkdir(baseDir+peopleSubdir) if not os.path.isdir(baseDir+peopleSubdir+'/'+handle): os.mkdir(baseDir+peopleSubdir+'/'+handle) + if not os.path.isdir(baseDir+peopleSubdir+'/'+handle+'/inbox'): + os.mkdir(baseDir+peopleSubdir+'/'+handle+'/inbox') + if not os.path.isdir(baseDir+peopleSubdir+'/'+handle+'/outbox'): + os.mkdir(baseDir+peopleSubdir+'/'+handle+'/outbox') + if not os.path.isdir(baseDir+peopleSubdir+'/'+handle+'/ocap'): + os.mkdir(baseDir+peopleSubdir+'/'+handle+'/ocap') + if not os.path.isdir(baseDir+peopleSubdir+'/'+handle+'/queue'): + os.mkdir(baseDir+peopleSubdir+'/'+handle+'/queue') filename=baseDir+peopleSubdir+'/'+handle+'.json' with open(filename, 'w') as fp: commentjson.dump(newPerson, fp, indent=4, sort_keys=False) diff --git a/tests.py b/tests.py index b647cf43..cad06654 100644 --- a/tests.py +++ b/tests.py @@ -36,13 +36,13 @@ from follow import unfollowPerson from follow import unfollowerOfPerson from follow import getFollowersOfPerson from follow import sendFollowRequest -from follow import getFollowersOfActor from person import createPerson from person import setPreferredNickname from person import setBio from auth import createBasicAuthHeader from auth import authorizeBasic from auth import storeBasicCredentials +from like import likePost testServerAliceRunning = False testServerBobRunning = False @@ -210,7 +210,6 @@ def testPostMessageBetweenServers(): httpPrefix='http' useTor=False - federationList=['127.0.0.50','127.0.0.100'] baseDir=os.getcwd() if os.path.isdir(baseDir+'/.tests'): @@ -223,12 +222,13 @@ def testPostMessageBetweenServers(): aliceDir=baseDir+'/.tests/alice' aliceDomain='127.0.0.50' alicePort=61935 - thrAlice = threadWithTrace(target=createServerAlice,args=(aliceDir,aliceDomain,alicePort,federationList,True,True,ocapAlways),daemon=True) - bobDir=baseDir+'/.tests/bob' bobDomain='127.0.0.100' bobPort=61936 - thrBob = threadWithTrace(target=createServerBob,args=(bobDir,bobDomain,bobPort,federationList,True,True,ocapAlways),daemon=True) + federationList=[bobDomain,aliceDomain] + + thrAlice = threadWithTrace(target=createServerAlice,args=(aliceDir,aliceDomain,alicePort,federationList,False,False,ocapAlways),daemon=True) + thrBob = threadWithTrace(target=createServerBob,args=(bobDir,bobDomain,bobPort,federationList,False,False,ocapAlways),daemon=True) thrAlice.start() thrBob.start() @@ -241,6 +241,7 @@ def testPostMessageBetweenServers(): time.sleep(1) + print('\n\n*******************************************************') print('Alice sends to Bob') os.chdir(aliceDir) sessionAlice = createSession(aliceDomain,alicePort,useTor) @@ -255,6 +256,11 @@ def testPostMessageBetweenServers(): ccUrl=None alicePersonCache={} aliceCachedWebfingers={} + + # nothing in Alice's outbox + outboxPath=aliceDir+'/accounts/alice@'+aliceDomain+'/outbox' + assert len([name for name in os.listdir(outboxPath) if os.path.isfile(os.path.join(outboxPath, name))])==0 + sendResult = sendPost(sessionAlice,aliceDir,'alice', aliceDomain, alicePort, 'bob', bobDomain, bobPort, ccUrl, httpPrefix, 'Why is a mouse when it spins?', followersOnly, saveToFile, clientToServer, federationList, aliceSendThreads, alicePostLog, aliceCachedWebfingers,alicePersonCache,inReplyTo, inReplyToAtomUri, subject) print('sendResult: '+str(sendResult)) @@ -263,9 +269,53 @@ def testPostMessageBetweenServers(): for i in range(30): if os.path.isdir(inboxPath): if len([name for name in os.listdir(inboxPath) if os.path.isfile(os.path.join(inboxPath, name))])>0: - break + if len([name for name in os.listdir(outboxPath) if os.path.isfile(os.path.join(outboxPath, name))])==1: + break time.sleep(1) + # inbox item created + assert len([name for name in os.listdir(inboxPath) if os.path.isfile(os.path.join(inboxPath, name))])==1 + # queue item removed + assert len([name for name in os.listdir(queuePath) if os.path.isfile(os.path.join(queuePath, name))])==0 + + #print('\n\n*******************************************************') + #print("Bob likes Alice's post") + + #followerOfPerson(bobDir,'bob',bobDomain,'alice',aliceDomain+':'+str(alicePort),federationList,True) + #followPerson(aliceDir,'alice',aliceDomain,'bob',bobDomain+':'+str(bobPort),federationList,True) + #followList=getFollowersOfPerson(bobDir,'bob',bobDomain,'followers.txt') + #assert len(followList)==1 + + #sessionBob = createSession(bobDomain,bobPort,useTor) + #bobSendThreads = [] + #bobPostLog = [] + #bobPersonCache={} + #bobCachedWebfingers={} + #statusNumber=None + #outboxPostFilename=None + #outboxPath=aliceDir+'/accounts/alice@'+aliceDomain+'/outbox' + #for name in os.listdir(outboxPath): + # if '#statuses#' in name: + # statusNumber=int(name.split('#statuses#')[1].replace('.json','')) + # outboxPostFilename=outboxPath+'/'+name + #assert statusNumber + #assert outboxPostFilename + #assert likePost(sessionBob,bobDir,federationList, \ + # 'bob',bobDomain,bobPort,httpPrefix, \ + # 'alice',aliceDomain,alicePort,[], \ + # statusNumber,False,bobSendThreads,bobPostLog, \ + # bobPersonCache,bobCachedWebfingers,True) + + #for i in range(20): + # if 'likes' in open(outboxPostFilename).read(): + # break + # time.sleep(1) + + #with open(outboxPostFilename, 'r') as fp: + # alicePostJson=commentjson.load(fp) + # pprint(alicePostJson) + #assert 'likes' in open(outboxPostFilename).read() + # stop the servers thrAlice.kill() thrAlice.join() @@ -275,11 +325,6 @@ def testPostMessageBetweenServers(): thrBob.join() assert thrBob.isAlive()==False - # inbox item created - assert len([name for name in os.listdir(inboxPath) if os.path.isfile(os.path.join(inboxPath, name))])==1 - # queue item removed - assert len([name for name in os.listdir(queuePath) if os.path.isfile(os.path.join(queuePath, name))])==0 - os.chdir(baseDir) shutil.rmtree(aliceDir) shutil.rmtree(bobDir) @@ -439,7 +484,7 @@ def testFollowBetweenServers(): assert aliceMessageArrived==True print('Message from Alice to Bob succeeded, since it was granted capabilities') - + print('\n\n*********************************************************') print("\nBob changes Alice's capabilities so that she can't reply on his posts") bobCapsFilename=bobDir+'/accounts/bob@'+bobDomain+'/ocap/accept/'+httpPrefix+':##'+aliceDomain+':'+str(alicePort)+'#users#alice.json' diff --git a/utils.py b/utils.py index a27f79b0..7292cbed 100644 --- a/utils.py +++ b/utils.py @@ -44,6 +44,8 @@ def createInboxQueueDir(nickname: str,domain: str,baseDir: str) -> str: def domainPermitted(domain: str, federationList: []): if len(federationList)==0: return True + if ':' in domain: + domain=domain.split(':')[0] if domain in federationList: return True return False @@ -91,6 +93,8 @@ def followPerson(baseDir: str,nickname: str, domain: str, \ if debug: print('DEBUG: follow of domain '+followDomain+' not permitted') return False + if debug: + print('DEBUG: follow of domain '+followDomain) handle=nickname.lower()+'@'+domain.lower() handleToFollow=followNickname.lower()+'@'+followDomain.lower() if not os.path.isdir(baseDir+'/accounts'): @@ -100,10 +104,33 @@ def followPerson(baseDir: str,nickname: str, domain: str, \ filename=baseDir+'/accounts/'+handle+'/'+followFile if os.path.isfile(filename): if handleToFollow in open(filename).read(): + if debug: + print('DEBUG: follow already exists') return True with open(filename, "a") as followfile: followfile.write(handleToFollow+'\n') + if debug: + print('DEBUG: follow added') return True + if debug: + print('DEBUG: creating new following file') with open(filename, "w") as followfile: followfile.write(handleToFollow+'\n') return True + +def locatePost(baseDir: str,nickname: str,domain: str,postUrl: str): + """Returns the filename for the given status post url + """ + boxName='outbox' + postFilename=baseDir+'/accounts/'+nickname+'@'+domain+'/'+boxName+'/'+postUrl.replace('/','#')+'.json' + if not os.path.isfile(postFilename): + # if this post in the inbox of the person? + boxName='inbox' + postFilename=baseDir+'/accounts/'+nickname+'@'+domain+'/'+boxName+'/'+postUrl.replace('/','#')+'.json' + if not os.path.isfile(postFilename): + # if this post in the shared inbox? + handle='inbox@'+domain + postFilename=baseDir+'/accounts/'+nickname+'@'+domain+'/'+boxName+'/'+postUrl.replace('/','#')+'.json' + if not os.path.isfile(postFilename): + postFilename=None + return postFilename