From 8cf1a6266a18f319b20af2257de8c637a7be166e Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 13 Jan 2020 10:35:17 +0000 Subject: [PATCH] Move outbox function to its own module --- daemon.py | 249 +---------------------------------------------- outbox.py | 281 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 282 insertions(+), 248 deletions(-) create mode 100644 outbox.py diff --git a/daemon.py b/daemon.py index e701e8630..1bcb41491 100644 --- a/daemon.py +++ b/daemon.py @@ -169,6 +169,7 @@ from httpsig import verifyPostHeaders from theme import setTheme from schedule import runPostSchedule from schedule import runPostScheduleWatchdog +from outbox import postMessageToOutbox import os import sys @@ -184,254 +185,6 @@ followsPerPage=12 # number of item shares per page sharesPerPage=12 -def postMessageToOutbox(messageJson: {},postToNickname: str, \ - server,baseDir: str,httpPrefix: str, \ - domain: str,domainFull: str,port: int, \ - recentPostsCache: {},followersThreads: [], \ - federationList: [],sendThreads: [], \ - postLog: [],cachedWebfingers: {}, \ - personCache: {},allowDeletion: bool, \ - useTor: bool,version: str,debug: bool) -> bool: - """post is received by the outbox - Client to server message post - https://www.w3.org/TR/activitypub/#client-to-server-outbox-delivery - """ - if not messageJson.get('type'): - if 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 debug: - print('DEBUG: POST to outbox - adding Create wrapper') - messageJson= \ - outboxMessageCreateWrap(httpPrefix, \ - postToNickname, \ - domain,port, \ - messageJson) - if messageJson['type']=='Create': - if not (messageJson.get('id') and \ - messageJson.get('type') and \ - messageJson.get('actor') and \ - messageJson.get('object') and \ - messageJson.get('to')): - if debug: - print('DEBUG: POST to outbox - Create does not have the required parameters') - return False - testDomain,testPort=getDomainFromActor(messageJson['actor']) - if testPort: - if testPort!=80 and testPort!=443: - testDomain=testDomain+':'+str(testPort) - if isBlockedDomain(baseDir,testDomain): - if debug: - print('DEBUG: domain is blocked: '+messageJson['actor']) - return False - # https://www.w3.org/TR/activitypub/#create-activity-outbox - messageJson['object']['attributedTo']=messageJson['actor'] - if messageJson['object'].get('attachment'): - attachmentIndex=0 - if messageJson['object']['attachment'][attachmentIndex].get('mediaType'): - fileExtension='png' - mediaTypeStr= \ - messageJson['object']['attachment'][attachmentIndex]['mediaType'] - if mediaTypeStr.endswith('jpeg'): - fileExtension='jpg' - elif mediaTypeStr.endswith('gif'): - fileExtension='gif' - elif mediaTypeStr.endswith('webp'): - fileExtension='webp' - elif mediaTypeStr.endswith('audio/mpeg'): - fileExtension='mp3' - elif mediaTypeStr.endswith('ogg'): - fileExtension='ogg' - elif mediaTypeStr.endswith('mp4'): - fileExtension='mp4' - elif mediaTypeStr.endswith('webm'): - fileExtension='webm' - elif mediaTypeStr.endswith('ogv'): - fileExtension='ogv' - mediaDir= \ - baseDir+'/accounts/'+ \ - postToNickname+'@'+domain - uploadMediaFilename=mediaDir+'/upload.'+fileExtension - if not os.path.isfile(uploadMediaFilename): - del messageJson['object']['attachment'] - else: - # generate a path for the uploaded image - mPath=getMediaPath() - mediaPath=mPath+'/'+createPassword(32)+'.'+fileExtension - createMediaDirs(baseDir,mPath) - mediaFilename=baseDir+'/'+mediaPath - # move the uploaded image to its new path - os.rename(uploadMediaFilename,mediaFilename) - # change the url of the attachment - messageJson['object']['attachment'][attachmentIndex]['url']= \ - httpPrefix+'://'+domainFull+ \ - '/'+mediaPath - - permittedOutboxTypes=[ - 'Create','Announce','Like','Follow','Undo', \ - 'Update','Add','Remove','Block','Delete', \ - 'Delegate','Skill','Bookmark' - ] - if messageJson['type'] not in permittedOutboxTypes: - if debug: - print('DEBUG: POST to outbox - '+messageJson['type']+ \ - ' is not a permitted activity type') - return False - if messageJson.get('id'): - postId=messageJson['id'].replace('/activity','').replace('/undo','') - if debug: - print('DEBUG: id attribute exists within POST to outbox') - else: - if debug: - print('DEBUG: No id attribute within POST to outbox') - postId=None - if debug: - print('DEBUG: savePostToBox') - if messageJson['type']!='Upgrade': - savedFilename= \ - savePostToBox(baseDir, \ - httpPrefix, \ - postId, \ - postToNickname, \ - domainFull,messageJson,'outbox') - if messageJson['type']=='Create' or \ - messageJson['type']=='Question' or \ - messageJson['type']=='Note' or \ - messageJson['type']=='Announce': - inboxUpdateIndex('outbox',baseDir, \ - postToNickname+'@'+domain, \ - savedFilename,debug) - if outboxAnnounce(recentPostsCache, \ - baseDir,messageJson,debug): - if debug: - print('DEBUG: Updated announcements (shares) collection for the post associated with the Announce activity') - if not server.session: - if debug: - print('DEBUG: creating new session for c2s') - server.session= \ - createSession(useTor) - if debug: - print('DEBUG: sending c2s post to followers') - # remove inactive threads - inactiveFollowerThreads=[] - for th in followersThreads: - if not th.is_alive(): - inactiveFollowerThreads.append(th) - for th in inactiveFollowerThreads: - followersThreads.remove(th) - if debug: - print('DEBUG: '+str(len(followersThreads))+' followers threads active') - # retain up to 20 threads - if len(followersThreads)>20: - # kill the thread if it is still alive - if followersThreads[0].is_alive(): - followersThreads[0].kill() - # remove it from the list - followersThreads.pop(0) - # create a thread to send the post to followers - followersThread= \ - sendToFollowersThread(server.session, \ - baseDir, \ - postToNickname, \ - domain, \ - port, \ - httpPrefix, \ - federationList, \ - sendThreads, \ - postLog, \ - cachedWebfingers, \ - personCache, \ - messageJson,debug, \ - version) - followersThreads.append(followersThread) - if debug: - print('DEBUG: handle any unfollow requests') - outboxUndoFollow(baseDir,messageJson,debug) - if debug: - print('DEBUG: handle delegation requests') - outboxDelegate(baseDir,postToNickname,messageJson,debug) - if debug: - print('DEBUG: handle skills changes requests') - outboxSkills(baseDir,postToNickname,messageJson,debug) - if debug: - print('DEBUG: handle availability changes requests') - outboxAvailability(baseDir,postToNickname,messageJson,debug) - - if debug: - print('DEBUG: handle any like requests') - outboxLike(recentPostsCache, \ - baseDir,httpPrefix, \ - postToNickname,domain,port, \ - messageJson,debug) - if debug: - print('DEBUG: handle any undo like requests') - outboxUndoLike(baseDir,httpPrefix, \ - postToNickname,domain,port, \ - messageJson,debug) - - if debug: - print('DEBUG: handle any bookmark requests') - outboxBookmark(recentPostsCache, \ - baseDir,httpPrefix, \ - postToNickname,domain,port, \ - messageJson,debug) - if debug: - print('DEBUG: handle any undo bookmark requests') - outboxUndoBookmark(recentPostsCache, \ - baseDir,httpPrefix, \ - postToNickname,domain,port, \ - messageJson,debug) - - if debug: - print('DEBUG: handle delete requests') - outboxDelete(baseDir,httpPrefix, \ - postToNickname,domain, \ - messageJson,debug, \ - allowDeletion) - if debug: - print('DEBUG: handle block requests') - outboxBlock(baseDir,httpPrefix, \ - postToNickname,domain, \ - port, - messageJson,debug) - if debug: - print('DEBUG: handle undo block requests') - outboxUndoBlock(baseDir,httpPrefix, \ - postToNickname,domain, \ - port, - messageJson,debug) - if debug: - print('DEBUG: handle share uploads') - outboxShareUpload(baseDir,httpPrefix, \ - postToNickname,domain, \ - port, - messageJson,debug) - if debug: - print('DEBUG: handle undo share uploads') - outboxUndoShareUpload(baseDir,httpPrefix, \ - postToNickname,domain, \ - port, - messageJson,debug) - if debug: - print('DEBUG: sending c2s post to named addresses') - print('c2s sender: '+postToNickname+'@'+ \ - domain+':'+str(port)) - sendToNamedAddresses(server.session,baseDir, \ - postToNickname,domain, \ - port, \ - httpPrefix, \ - federationList, \ - sendThreads, \ - postLog, \ - cachedWebfingers, \ - personCache, \ - messageJson,debug, \ - version) - return True - def readFollowList(filename: str) -> None: """Returns a list of ActivityPub addresses to follow """ diff --git a/outbox.py b/outbox.py new file mode 100644 index 000000000..240f212a9 --- /dev/null +++ b/outbox.py @@ -0,0 +1,281 @@ +__filename__ = "outbox.py" +__author__ = "Bob Mottram" +__license__ = "AGPL3+" +__version__ = "1.1.0" +__maintainer__ = "Bob Mottram" +__email__ = "bob@freedombone.net" +__status__ = "Production" + +import os +import json +from session import createSession +from posts import outboxMessageCreateWrap +from posts import savePostToBox +from posts import sendToFollowersThread +from posts import sendToNamedAddresses +from utils import getDomainFromActor +from blocking import isBlockedDomain +from blocking import outboxBlock +from blocking import outboxUndoBlock +from media import getMediaPath +from media import createMediaDirs +from inbox import inboxUpdateIndex +from announce import outboxAnnounce +from follow import outboxUndoFollow +from roles import outboxDelegate +from skills import outboxSkills +from availability import outboxAvailability +from like import outboxLike +from like import outboxUndoLike +from bookmarks import outboxBookmark +from bookmarks import outboxUndoBookmark +from delete import outboxDelete +from shares import outboxShareUpload +from shares import outboxUndoShareUpload + +def postMessageToOutbox(messageJson: {},postToNickname: str, \ + server,baseDir: str,httpPrefix: str, \ + domain: str,domainFull: str,port: int, \ + recentPostsCache: {},followersThreads: [], \ + federationList: [],sendThreads: [], \ + postLog: [],cachedWebfingers: {}, \ + personCache: {},allowDeletion: bool, \ + useTor: bool,version: str,debug: bool) -> bool: + """post is received by the outbox + Client to server message post + https://www.w3.org/TR/activitypub/#client-to-server-outbox-delivery + """ + if not messageJson.get('type'): + if 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 debug: + print('DEBUG: POST to outbox - adding Create wrapper') + messageJson= \ + outboxMessageCreateWrap(httpPrefix, \ + postToNickname, \ + domain,port, \ + messageJson) + if messageJson['type']=='Create': + if not (messageJson.get('id') and \ + messageJson.get('type') and \ + messageJson.get('actor') and \ + messageJson.get('object') and \ + messageJson.get('to')): + if debug: + print('DEBUG: POST to outbox - Create does not have the required parameters') + return False + testDomain,testPort=getDomainFromActor(messageJson['actor']) + if testPort: + if testPort!=80 and testPort!=443: + testDomain=testDomain+':'+str(testPort) + if isBlockedDomain(baseDir,testDomain): + if debug: + print('DEBUG: domain is blocked: '+messageJson['actor']) + return False + # https://www.w3.org/TR/activitypub/#create-activity-outbox + messageJson['object']['attributedTo']=messageJson['actor'] + if messageJson['object'].get('attachment'): + attachmentIndex=0 + if messageJson['object']['attachment'][attachmentIndex].get('mediaType'): + fileExtension='png' + mediaTypeStr= \ + messageJson['object']['attachment'][attachmentIndex]['mediaType'] + if mediaTypeStr.endswith('jpeg'): + fileExtension='jpg' + elif mediaTypeStr.endswith('gif'): + fileExtension='gif' + elif mediaTypeStr.endswith('webp'): + fileExtension='webp' + elif mediaTypeStr.endswith('audio/mpeg'): + fileExtension='mp3' + elif mediaTypeStr.endswith('ogg'): + fileExtension='ogg' + elif mediaTypeStr.endswith('mp4'): + fileExtension='mp4' + elif mediaTypeStr.endswith('webm'): + fileExtension='webm' + elif mediaTypeStr.endswith('ogv'): + fileExtension='ogv' + mediaDir= \ + baseDir+'/accounts/'+ \ + postToNickname+'@'+domain + uploadMediaFilename=mediaDir+'/upload.'+fileExtension + if not os.path.isfile(uploadMediaFilename): + del messageJson['object']['attachment'] + else: + # generate a path for the uploaded image + mPath=getMediaPath() + mediaPath=mPath+'/'+createPassword(32)+'.'+fileExtension + createMediaDirs(baseDir,mPath) + mediaFilename=baseDir+'/'+mediaPath + # move the uploaded image to its new path + os.rename(uploadMediaFilename,mediaFilename) + # change the url of the attachment + messageJson['object']['attachment'][attachmentIndex]['url']= \ + httpPrefix+'://'+domainFull+'/'+mediaPath + + permittedOutboxTypes=[ + 'Create','Announce','Like','Follow','Undo', \ + 'Update','Add','Remove','Block','Delete', \ + 'Delegate','Skill','Bookmark' + ] + if messageJson['type'] not in permittedOutboxTypes: + if debug: + print('DEBUG: POST to outbox - '+messageJson['type']+ \ + ' is not a permitted activity type') + return False + if messageJson.get('id'): + postId=messageJson['id'].replace('/activity','').replace('/undo','') + if debug: + print('DEBUG: id attribute exists within POST to outbox') + else: + if debug: + print('DEBUG: No id attribute within POST to outbox') + postId=None + if debug: + print('DEBUG: savePostToBox') + if messageJson['type']!='Upgrade': + savedFilename= \ + savePostToBox(baseDir, \ + httpPrefix, \ + postId, \ + postToNickname, \ + domainFull,messageJson,'outbox') + if messageJson['type']=='Create' or \ + messageJson['type']=='Question' or \ + messageJson['type']=='Note' or \ + messageJson['type']=='Announce': + inboxUpdateIndex('outbox',baseDir, \ + postToNickname+'@'+domain, \ + savedFilename,debug) + if outboxAnnounce(recentPostsCache, \ + baseDir,messageJson,debug): + if debug: + print('DEBUG: Updated announcements (shares) collection for the post associated with the Announce activity') + if not server.session: + if debug: + print('DEBUG: creating new session for c2s') + server.session= \ + createSession(useTor) + if debug: + print('DEBUG: sending c2s post to followers') + # remove inactive threads + inactiveFollowerThreads=[] + for th in followersThreads: + if not th.is_alive(): + inactiveFollowerThreads.append(th) + for th in inactiveFollowerThreads: + followersThreads.remove(th) + if debug: + print('DEBUG: '+str(len(followersThreads))+' followers threads active') + # retain up to 20 threads + if len(followersThreads)>20: + # kill the thread if it is still alive + if followersThreads[0].is_alive(): + followersThreads[0].kill() + # remove it from the list + followersThreads.pop(0) + # create a thread to send the post to followers + followersThread= \ + sendToFollowersThread(server.session, \ + baseDir, \ + postToNickname, \ + domain, \ + port, \ + httpPrefix, \ + federationList, \ + sendThreads, \ + postLog, \ + cachedWebfingers, \ + personCache, \ + messageJson,debug, \ + version) + followersThreads.append(followersThread) + if debug: + print('DEBUG: handle any unfollow requests') + outboxUndoFollow(baseDir,messageJson,debug) + if debug: + print('DEBUG: handle delegation requests') + outboxDelegate(baseDir,postToNickname,messageJson,debug) + if debug: + print('DEBUG: handle skills changes requests') + outboxSkills(baseDir,postToNickname,messageJson,debug) + if debug: + print('DEBUG: handle availability changes requests') + outboxAvailability(baseDir,postToNickname,messageJson,debug) + + if debug: + print('DEBUG: handle any like requests') + outboxLike(recentPostsCache, \ + baseDir,httpPrefix, \ + postToNickname,domain,port, \ + messageJson,debug) + if debug: + print('DEBUG: handle any undo like requests') + outboxUndoLike(baseDir,httpPrefix, \ + postToNickname,domain,port, \ + messageJson,debug) + + if debug: + print('DEBUG: handle any bookmark requests') + outboxBookmark(recentPostsCache, \ + baseDir,httpPrefix, \ + postToNickname,domain,port, \ + messageJson,debug) + if debug: + print('DEBUG: handle any undo bookmark requests') + outboxUndoBookmark(recentPostsCache, \ + baseDir,httpPrefix, \ + postToNickname,domain,port, \ + messageJson,debug) + + if debug: + print('DEBUG: handle delete requests') + outboxDelete(baseDir,httpPrefix, \ + postToNickname,domain, \ + messageJson,debug, \ + allowDeletion) + if debug: + print('DEBUG: handle block requests') + outboxBlock(baseDir,httpPrefix, \ + postToNickname,domain, \ + port, + messageJson,debug) + if debug: + print('DEBUG: handle undo block requests') + outboxUndoBlock(baseDir,httpPrefix, \ + postToNickname,domain, \ + port, + messageJson,debug) + if debug: + print('DEBUG: handle share uploads') + outboxShareUpload(baseDir,httpPrefix, \ + postToNickname,domain, \ + port, + messageJson,debug) + if debug: + print('DEBUG: handle undo share uploads') + outboxUndoShareUpload(baseDir,httpPrefix, \ + postToNickname,domain, \ + port, + messageJson,debug) + if debug: + print('DEBUG: sending c2s post to named addresses') + print('c2s sender: '+postToNickname+'@'+ \ + domain+':'+str(port)) + sendToNamedAddresses(server.session,baseDir, \ + postToNickname,domain, \ + port, \ + httpPrefix, \ + federationList, \ + sendThreads, \ + postLog, \ + cachedWebfingers, \ + personCache, \ + messageJson,debug, \ + version) + return True