epicyon/inbox.py

170 lines
5.6 KiB
Python
Raw Normal View History

2019-06-28 21:59:54 +00:00
__filename__ = "inbox.py"
__author__ = "Bob Mottram"
__license__ = "AGPL3+"
__version__ = "0.0.1"
__maintainer__ = "Bob Mottram"
__email__ = "bob@freedombone.net"
__status__ = "Production"
import json
import os
2019-06-29 10:08:59 +00:00
import datetime
2019-07-04 12:23:53 +00:00
import time
import json
import commentjson
2019-07-02 10:39:55 +00:00
from utils import urlPermitted
2019-07-04 10:02:56 +00:00
from utils import createInboxQueueDir
2019-07-04 12:23:53 +00:00
from posts import getPersonPubKey
from httpsig import verifyPostHeaders
from session import createSession
from follow import receiveFollowRequest
2019-06-28 21:59:54 +00:00
2019-07-02 15:07:27 +00:00
def inboxMessageHasParams(messageJson: {}) -> bool:
"""Checks whether an incoming message contains expected parameters
"""
expectedParams=['type','to','actor','object']
for param in expectedParams:
if not messageJson.get(param):
return False
return True
2019-07-01 11:48:54 +00:00
def inboxPermittedMessage(domain: str,messageJson: {},federationList: []) -> bool:
2019-06-28 21:59:54 +00:00
""" check that we are receiving from a permitted domain
"""
testParam='actor'
if not messageJson.get(testParam):
return False
actor=messageJson[testParam]
# always allow the local domain
2019-07-01 11:48:54 +00:00
if domain in actor:
2019-06-28 21:59:54 +00:00
return True
2019-07-02 10:39:55 +00:00
if not urlPermitted(actor,federationList):
2019-06-28 21:59:54 +00:00
return False
if messageJson.get('object'):
if messageJson['object'].get('inReplyTo'):
inReplyTo=messageJson['object']['inReplyTo']
2019-07-02 10:39:55 +00:00
if not urlPermitted(inReplyTo, federationList):
2019-06-28 21:59:54 +00:00
return False
return True
2019-06-29 10:08:59 +00:00
2019-07-02 20:54:22 +00:00
def validPublishedDate(published) -> bool:
2019-06-29 10:08:59 +00:00
currTime=datetime.datetime.utcnow()
pubDate=datetime.datetime.strptime(published,"%Y-%m-%dT%H:%M:%SZ")
daysSincePublished = (currTime - pubTime).days
if daysSincePublished>30:
return False
return True
2019-07-04 10:02:56 +00:00
2019-07-04 12:23:53 +00:00
def savePostToInboxQueue(baseDir: str,httpPrefix: str,keyId: str,nickname: str, domain: str,postJson: {},headers: {}) -> str:
2019-07-04 10:02:56 +00:00
"""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','')
2019-07-04 10:09:27 +00:00
currTime=datetime.datetime.utcnow()
published=currTime.strftime("%Y-%m-%dT%H:%M:%SZ")
inboxQueueDir = createInboxQueueDir(nickname,domain,baseDir)
handle=nickname+'@'+domain
destination=baseDir+'/accounts/'+handle+'/inbox/'+postId.replace('/','#')+'.json'
if os.path.isfile(destination):
# inbox item already exists
return None
filename=inboxQueueDir+'/'+postId.replace('/','#')+'.json'
2019-07-04 10:02:56 +00:00
newBufferItem = {
2019-07-04 10:09:27 +00:00
'published': published,
2019-07-04 10:02:56 +00:00
'keyId': keyid,
2019-07-04 12:23:53 +00:00
'headers': headers,
'post': postJson,
'filename': filename,
'destination': destination
2019-07-04 10:02:56 +00:00
}
with open(filename, 'w') as fp:
commentjson.dump(newQueueItem, fp, indent=4, sort_keys=False)
return filename
2019-07-04 12:23:53 +00:00
def runInboxQueue(baseDir: str,httpPrefix: str,personCache: {},queue: [],domain: str,port: int,useTor: bool,federationList: [],debug: bool) -> None:
"""Processes received items and moves them to
the appropriate directories
"""
currSessionTime=int(time.time())
sessionLastUpdate=currSessionTime
session=createSession(domain,port,useTor)
if debug:
print('DEBUG: Inbox queue running')
while True:
if len(queue)>0:
currSessionTime=int(time.time())
if currSessionTime-sessionLastUpdate>1200:
session=createSession(domain,port,useTor)
sessionLastUpdate=currSessionTime
# oldest item first
queue.sort()
queueFilename=queue[0]
if not os.path.isfile(queueFilename):
if debug:
print("DEBUG: queue item rejected becase it has no file: "+queueFilename)
queue.pop(0)
continue
# Load the queue json
with open(queueFilename, 'r') as fp:
queueJson=commentjson.load(fp)
# Try a few times to obtain teh public key
pubKey=None
for tries in range(5):
pubKey=getPersonPubKey(session,queueJson['keyId'],personCache)
if not pubKey:
if debug:
print('DEBUG: Retry '+str(tries+1)+' obtaining public key for '+queueJson['keyId'])
time.sleep(5)
if not pubKey:
if debug:
print('DEBUG: public key could not be obtained from '+queueJson['keyId'])
os.remove(queueFilename)
queue.pop(0)
continue
# check the signature
if not verifyPostHeaders(httpPrefix, \
pubKey, queueJson.headers, \
'/inbox', False, \
json.dumps(messageJson)):
if debug:
print('DEBUG: Header signature check failed')
os.remove(queueFilename)
queue.pop(0)
continue
if receiveFollowRequest(baseDir, \
queueJson.post, \
federationList):
if debug:
print('DEBUG: Follow accepted from '+queueJson['keyId'])
os.remove(queueFilename)
queue.pop(0)
continue
if debug:
print('DEBUG: Queue post accepted')
# move to the destination inbox
os.rename(queueFilename,queueJson['destination'])
queue.pop(0)
time.sleep(2)