diff --git a/daemon.py b/daemon.py index f3247731e..52a0cecda 100644 --- a/daemon.py +++ b/daemon.py @@ -107,12 +107,14 @@ class PubServer(BaseHTTPRequestHandler): https://www.w3.org/TR/activitypub/#client-to-server-outbox-delivery """ if not messageJson.get('type'): + if self.server.debug: + print('DEBUG: POST to outbox has no "type" parameter') return False if not messageJson.get('object') and messageJson.get('content'): if messageJson['type']!='Create': # https://www.w3.org/TR/activitypub/#object-without-create if self.server.debug: - print('DEBUG POST to outbox: Adding Create wrapper') + print('DEBUG: POST to outbox - adding Create wrapper') messageJson= \ outboxMessageCreateWrap(self.server.httpPrefix, \ self.postToNickname, \ @@ -125,14 +127,14 @@ class PubServer(BaseHTTPRequestHandler): messageJson.get('atomUri') and \ messageJson.get('to')): if self.server.debug: - print('DEBUG POST to outbox: Create does not have the required parameters') + print('DEBUG: POST to outbox - Create does not have the required parameters') return False # https://www.w3.org/TR/activitypub/#create-activity-outbox messageJson['object']['attributedTo']=messageJson['actor'] permittedOutboxTypes=['Create','Announce','Like','Follow','Undo','Update','Add','Remove','Block','Delete'] if messageJson['type'] not in permittedOutboxTypes: if self.server.debug: - print('DEBUG POST to outbox: '+messageJson['type']+' is not a permitted activity type') + print('DEBUG: POST to outbox - '+messageJson['type']+' is not a permitted activity type') return False if messageJson.get('id'): postId=messageJson['id'] @@ -283,12 +285,14 @@ class PubServer(BaseHTTPRequestHandler): # if this is a POST to teh outbox then check authentication self.outboxAuthenticated=False self.postToNickname=None + if self.path.endswith('/outbox'): if '/users/' in self.path: if self.headers.get('Authorization'): if authorize(self.server.baseDir,self.path,self.headers['Authorization'],self.server.debug): self.outboxAuthenticated=True - self.postToNickname=nickname + pathUsersSection=path.split('/users/')[1] + self.postToNickname=pathUsersSection.split('/')[0] # TODO print('c2s posts not supported yet') self.send_response(405) @@ -360,9 +364,39 @@ class PubServer(BaseHTTPRequestHandler): pprint(messageJson) + if not headers.get('keyId'): + if self.server.debug: + print('DEBUG: POST to inbox has no keyId in header') + return + if self.server.debug: - print('DEBUG: POST create session') + print('DEBUG: POST saving to inbox cache') + if '/users/' in self.path: + pathUsersSection=self.path.split('/users/')[1] + if '/' not in pathUsersSection: + if self.server.debug: + print('DEBUG: This is not a users endpoint') + else: + self.postToNickname=pathUsersSection.split('/')[0] + if self.postToNickname: + cacheFilename = \ + savePostToInboxQueue(self.server.baseDir, \ + self.server.httpPrefix, \ + headers['keyId'], \ + self.postToNickname, \ + self.server.domain, \ + messageJson) + if cacheFilename: + if cacheFilename not in self.server.inboxQueue: + self.server.inboxQueue.append(cacheFilename) + return + else: + print('DEBUG: POST to shared inbox') + return + + + currSessionTime=int(time.time()) if currSessionTime-self.server.sessionLastUpdate>1200: self.server.sessionLastUpdate=currSessionTime @@ -449,5 +483,6 @@ def runDaemon(domain: str,port=80,httpPrefix='https',fedList=[],useTor=False,deb httpd.GETbusy=False httpd.POSTbusy=False httpd.receivedMessage=False + httpd.inboxQueue=[] print('Running ActivityPub daemon on ' + domain + ' port ' + str(port)) httpd.serve_forever() diff --git a/inbox.py b/inbox.py index 8164c9a98..d7d304644 100644 --- a/inbox.py +++ b/inbox.py @@ -10,6 +10,7 @@ import json import os import datetime from utils import urlPermitted +from utils import createInboxQueueDir def inboxMessageHasParams(messageJson: {}) -> bool: """Checks whether an incoming message contains expected parameters @@ -49,3 +50,26 @@ def validPublishedDate(published) -> bool: if daysSincePublished>30: return False return True + +def savePostToInboxQueue(baseDir: str,httpPrefix: str,keyId: str,nickname: str, domain: str,postJson: {}) -> str: + """Saves the give json to the inbox queue for the person + keyId specifies the actor sending the post + """ + if ':' in domain: + domain=domain.split(':')[0] + if not keyId: + return None + if not postJson.get('id'): + return None + postId=postJson['id'].replace('/activity','') + + newBufferItem = { + 'keyId': keyid, + 'post': postJson + } + + inboxQueueDir = createInboxQueueDir(nickname,domain,baseDir) + filename=inboxQueueDir+'/'+postId.replace('/','#')+'.json' + with open(filename, 'w') as fp: + commentjson.dump(newQueueItem, fp, indent=4, sort_keys=False) + return filename diff --git a/utils.py b/utils.py index 1d464aa4c..dd2e28eab 100644 --- a/utils.py +++ b/utils.py @@ -20,16 +20,26 @@ def getStatusNumber() -> (str,str): conversationDate=currTime.strftime("%Y-%m-%d") return statusNumber,published -def createOutboxDir(nickname: str,domain: str,baseDir: str) -> str: - """Create an outbox for a person and returns the feed filename and directory +def createPersonDir(nickname: str,domain: str,baseDir: str,dirname: str) -> str: + """Create a directory for a person """ handle=nickname.lower()+'@'+domain.lower() if not os.path.isdir(baseDir+'/accounts/'+handle): os.mkdir(baseDir+'/accounts/'+handle) - outboxDir=baseDir+'/accounts/'+handle+'/outbox' - if not os.path.isdir(outboxDir): - os.mkdir(outboxDir) - return outboxDir + boxDir=baseDir+'/accounts/'+handle+'/'+dirname + if not os.path.isdir(boxDir): + os.mkdir(boxDir) + return boxDir + +def createOutboxDir(nickname: str,domain: str,baseDir: str) -> str: + """Create an outbox for a person + """ + return createPersonDir(nickname,domain,baseDir,'outbox') + +def createInboxQueueDir(nickname: str,domain: str,baseDir: str) -> str: + """Create an inbox queue and returns the feed filename and directory + """ + return createPersonDir(nickname,domain,baseDir,'queue') def domainPermitted(domain: str, federationList: []): if len(federationList)==0: