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-07-04 14:36:29 +00:00
|
|
|
from pprint import pprint
|
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 14:36:29 +00:00
|
|
|
def savePostToInboxQueue(baseDir: str,httpPrefix: str,nickname: str, domain: str,postJson: {},headers: str) -> 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 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")
|
2019-07-04 10:19:15 +00:00
|
|
|
|
|
|
|
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 14:36:29 +00:00
|
|
|
newQueueItem = {
|
2019-07-04 10:09:27 +00:00
|
|
|
'published': published,
|
2019-07-04 12:23:53 +00:00
|
|
|
'headers': headers,
|
2019-07-04 10:19:15 +00:00
|
|
|
'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
|
2019-07-04 17:31:41 +00:00
|
|
|
for tries in range(8):
|
2019-07-04 14:36:29 +00:00
|
|
|
keyId=None
|
|
|
|
signatureParams=queueJson['headers'].split(',')
|
|
|
|
for signatureItem in signatureParams:
|
|
|
|
if signatureItem.startswith('keyId='):
|
|
|
|
if '"' in signatureItem:
|
|
|
|
keyId=signatureItem.split('"')[1]
|
|
|
|
break
|
|
|
|
if not keyId:
|
|
|
|
if debug:
|
|
|
|
print('DEBUG: No keyId in signature: '+queueJson['headers']['signature'])
|
|
|
|
os.remove(queueFilename)
|
|
|
|
queue.pop(0)
|
|
|
|
continue
|
|
|
|
|
|
|
|
pubKey=getPersonPubKey(session,keyId,personCache,debug)
|
2019-07-04 17:31:41 +00:00
|
|
|
if pubKey:
|
|
|
|
print('DEBUG: public key: '+str(pubKey))
|
|
|
|
break
|
|
|
|
|
|
|
|
if debug:
|
|
|
|
print('DEBUG: Retry '+str(tries+1)+' obtaining public key for '+keyId)
|
|
|
|
time.sleep(5)
|
|
|
|
|
2019-07-04 12:23:53 +00:00
|
|
|
if not pubKey:
|
|
|
|
if debug:
|
2019-07-04 17:31:41 +00:00
|
|
|
print('DEBUG: public key could not be obtained from '+keyId)
|
2019-07-04 12:23:53 +00:00
|
|
|
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
|
|
|
|
|
2019-07-04 17:31:41 +00:00
|
|
|
if debug:
|
|
|
|
print('DEBUG: Signature check success')
|
|
|
|
|
2019-07-04 12:23:53 +00:00
|
|
|
if receiveFollowRequest(baseDir, \
|
|
|
|
queueJson.post, \
|
|
|
|
federationList):
|
|
|
|
|
|
|
|
if debug:
|
2019-07-04 17:31:41 +00:00
|
|
|
print('DEBUG: Follow accepted from '+keyId)
|
2019-07-04 12:23:53 +00:00
|
|
|
os.remove(queueFilename)
|
|
|
|
queue.pop(0)
|
|
|
|
continue
|
|
|
|
|
|
|
|
if debug:
|
|
|
|
print('DEBUG: Queue post accepted')
|
2019-07-04 17:31:41 +00:00
|
|
|
|
2019-07-04 12:23:53 +00:00
|
|
|
# move to the destination inbox
|
|
|
|
os.rename(queueFilename,queueJson['destination'])
|
|
|
|
queue.pop(0)
|
|
|
|
time.sleep(2)
|