diff --git a/daemon.py b/daemon.py index c60bd65f..c445b6ba 100644 --- a/daemon.py +++ b/daemon.py @@ -30,6 +30,9 @@ from posts import savePostToBox from posts import sendToFollowers from posts import postIsAddressedToPublic from posts import sendToNamedAddresses +from posts import createPublicPost +from posts import createFollowersOnlyPost +from posts import createDirectMessagePost from inbox import inboxPermittedMessage from inbox import inboxMessageHasParams from inbox import runInboxQueue @@ -1068,6 +1071,7 @@ class PubServer(BaseHTTPRequestHandler): # email style encoding message/rfc822 messageFields=msg.get_payload(decode=False).split(boundary) fields={} + filename=None for f in messageFields: if ' name="' in f: postStr=f.split(' name="',1)[1] @@ -1089,7 +1093,6 @@ class PubServer(BaseHTTPRequestHandler): # of an image searchStr=b'Content-Type: image/png' imageLocation=postBytes.find(searchStr) - filename=None filenameBase=self.server.baseDir+'/accounts/'+nickname+'@'+self.server.domain+'/upload' if imageLocation: filename=filenameBase+'.png' @@ -1117,43 +1120,68 @@ class PubServer(BaseHTTPRequestHandler): fd.write(postBytes[startPos:]) fd.close() - postSubject=None - postMessage=None - postImageDescription=None - postImage=None - postType=None - postCategory=None - postLocation=None - for postKey,postValue in fields.items(): - if postKey=='subject': - postSubject=postValue - elif postKey=='message': - postMessage=postValue - elif postKey=='imageDescription': - postImageDescription=postValue - elif postKey=='postType': - postType=postValue - elif postKey=='category': - postCategory=postValue - elif postKey=='location': - postLocation=postValue - if self.server.debug: - if postSubject: - print('subject: '+postSubject) - if postMessage: - print('message: '+postMessage) - if postImageDescription: - print('image description: '+postImageDescription) - if postType: - print('type: '+postType) - if postCategory: - print('category: '+postCategory) - if postLocation: - print('location: '+postLocation) - if not postMessage: - return False - return True - return False + # send the post + + if not fields.get('message'): + return False + + if not fields.get('imageDescription'): + fields['imageDescription']=None + if not fields.get('subject'): + fields['subject']=None + if not fields.get('replyTo'): + fields['replyTo']=None + + if postType=='newpost': + messageJson= \ + createPublicPost(self.server.baseDir, \ + nickname, \ + self.server.domain,self.server.port, \ + self.server.httpPrefix, \ + fields['message'],False,False,False, \ + filename,fields['imageDescription'],True, \ + fields['replyTo'], fields['replyTo'],fields['subject']) + if messageJson: + queueStatus=self._updateInboxQueue(nickname,messageJson) + if queueStatus==0: + return True + + if postType=='newfollowers': + messageJson= \ + createFollowersOnlyPost(self.server.baseDir, \ + nickname, \ + self.server.domain,self.server.port, \ + self.server.httpPrefix, \ + fields['message'],True,False,False, \ + filename,fields['imageDescription'],True, \ + fields['replyTo'], fields['replyTo'],fields['subject']) + if messageJson: + queueStatus=self._updateInboxQueue(nickname,messageJson) + if queueStatus==0: + return True + + if postType=='newdm': + messageJson= \ + createDirectMessagePost(self.server.baseDir, \ + nickname, \ + self.server.domain,self.server.port, \ + self.server.httpPrefix, \ + fields['message'],True,False,False, \ + filename,fields['imageDescription'],True, \ + fields['replyTo'], fields['replyTo'],fields['subject']) + if messageJson: + queueStatus=self._updateInboxQueue(nickname,messageJson) + if queueStatus==0: + return True + + if postType=='newunlisted': + # TODO + return True + + if postType=='newshare': + # TODO + return True + return False def do_POST(self): @@ -1449,7 +1477,7 @@ class PubServer(BaseHTTPRequestHandler): else: if self.path == '/sharedInbox' or self.path == '/inbox': print('DEBUG: POST to shared inbox') - queueStatus-_updateInboxQueue('inbox',messageJson) + queueStatus=_updateInboxQueue('inbox',messageJson) if queueStatus==0: self.send_response(200) self.end_headers() diff --git a/epicyon.py b/epicyon.py index 59def645..6b94bc90 100644 --- a/epicyon.py +++ b/epicyon.py @@ -11,7 +11,6 @@ from person import createSharedInbox from person import createCapabilitiesInbox from person import setPreferredNickname from person import setBio -from person import validNickname from person import setProfileImage from skills import setSkillLevel from roles import setRole @@ -44,7 +43,6 @@ from daemon import runDaemon import socket from follow import clearFollows from follow import clearFollowers -from utils import followPerson from follow import followerOfPerson from follow import unfollowPerson from follow import unfollowerOfPerson @@ -60,6 +58,8 @@ from auth import removePassword from auth import createPassword from utils import getDomainFromActor from utils import getNicknameFromActor +from utils import followPerson +from utils import validNickname from media import archiveMedia from delete import sendDeleteViaServer from like import sendLikeViaServer diff --git a/follow.py b/follow.py index 90134352..f711c29e 100644 --- a/follow.py +++ b/follow.py @@ -11,7 +11,7 @@ import commentjson from pprint import pprint import os import sys -from person import validNickname +from utils import validNickname from utils import domainPermitted from utils import getDomainFromActor from utils import getNicknameFromActor diff --git a/person.py b/person.py index 0dcd084e..02e47cea 100644 --- a/person.py +++ b/person.py @@ -22,6 +22,7 @@ from posts import createOutbox from auth import storeBasicCredentials from roles import setRole from media import removeMetaData +from utils import validNickname def generateRSAKey() -> (str,str): key = RSA.generate(2048) @@ -259,16 +260,6 @@ def createCapabilitiesInbox(baseDir: str,nickname: str,domain: str,port: int, \ """Generates the capabilities inbox to sign requests """ return createPersonBase(baseDir,nickname,domain,port,httpPrefix,True,None) - -def validNickname(nickname: str) -> bool: - forbiddenChars=['.',' ','/','?',':',';','@'] - for c in forbiddenChars: - if c in nickname: - return False - reservedNames=['inbox','outbox','following','followers','capabilities'] - if nickname in reservedNames: - return False - return True def personLookup(domain: str,path: str,baseDir: str) -> {}: """Lookup the person for an given nickname diff --git a/posts.py b/posts.py index ed1f7019..6f609fdd 100644 --- a/posts.py +++ b/posts.py @@ -35,6 +35,7 @@ from utils import urlPermitted from utils import getNicknameFromActor from utils import getDomainFromActor from utils import deletePost +from utils import validNickname from capabilities import getOcapFilename from capabilities import capabilitiesUpdate from media import attachImage @@ -591,7 +592,7 @@ def createPublicPost(baseDir: str, clientToServer: bool,\ attachImageFilename: str,imageDescription: str,useBlurhash: bool, \ inReplyTo=None, inReplyToAtomUri=None, subject=None) -> {}: - """Public post to the outbox + """Public post """ return createPostBase(baseDir,nickname, domain, port, \ 'https://www.w3.org/ns/activitystreams#Public', \ @@ -601,6 +602,71 @@ def createPublicPost(baseDir: str, attachImageFilename,imageDescription,useBlurhash, \ inReplyTo, inReplyToAtomUri, subject) +def createFollowersOnlyPost(baseDir: str, + nickname: str, domain: str, port: int,httpPrefix: str, \ + content: str, followersOnly: bool, saveToFile: bool, + clientToServer: bool,\ + attachImageFilename: str,imageDescription: str,useBlurhash: bool, \ + inReplyTo=None, inReplyToAtomUri=None, subject=None) -> {}: + """Followers only post + """ + return createPostBase(baseDir,nickname, domain, port, \ + httpPrefix+'://'+domain+'/users/'+nickname+'/followers', \ + None, + httpPrefix, content, followersOnly, saveToFile, \ + clientToServer, \ + attachImageFilename,imageDescription,useBlurhash, \ + inReplyTo, inReplyToAtomUri, subject) + +def getMentionedPeople(baseDir: str,httpPrefix: str,content: str,domain: str) -> []: + """Extracts a list of mentioned actors from the given message content + """ + if '@' not in content: + return None + mentions=[] + words=content.split(' ') + for wrd in words: + if wrd.startswith('@'): + handle=wrd[1:] + if '@' not in handle: + handle=handle+'@'+domain + if not os.path.isdir(baseDir+'/accounts/'+handle): + continue + else: + externalDomain=handle.split('@')[1] + if not ('.' in externalDomain or externalDomain=='localhost'): + continue + mentionedNickname=handle.split('@')[0] + if not validNickname(mentionedNickname): + continue + actor=httpPrefix+'://'+handle.split('@')[1]+'/users/'+mentionedNickname + mentions.append(actor) + return actor + +def createDirectMessagePost(baseDir: str, + nickname: str, domain: str, port: int,httpPrefix: str, \ + content: str, followersOnly: bool, saveToFile: bool, + clientToServer: bool,\ + attachImageFilename: str,imageDescription: str,useBlurhash: bool, \ + inReplyTo=None, inReplyToAtomUri=None, subject=None) -> {}: + """Direct Message post + """ + mentionedPeople=getMentionedPeople(baseDir,httpPrefix,content,domain) + postTo=None + postCc=None + if not mentionedPeople: + return None + if len(mentionedPeople)==1: + postTo=mentionedPeople[0] + if len(mentionedPeople)>1: + postCc=mentionedPeople[1] + return createPostBase(baseDir,nickname, domain, port, \ + postTo,postCc, \ + httpPrefix, content, followersOnly, saveToFile, \ + clientToServer, \ + attachImageFilename,imageDescription,useBlurhash, \ + inReplyTo, inReplyToAtomUri, subject) + def threadSendPost(session,postJsonObject: {},federationList: [],\ inboxUrl: str, baseDir: str,signatureHeaderJson: {},postLog: [], debug :bool) -> None: diff --git a/shares.py b/shares.py index e35f59c4..19d63399 100644 --- a/shares.py +++ b/shares.py @@ -11,11 +11,11 @@ import commentjson import os import time from shutil import copyfile -from person import validNickname from webfinger import webfingerHandle from auth import createBasicAuthHeader from posts import getPersonBox from session import postJson +from utils import validNickname from utils import getNicknameFromActor from utils import getDomainFromActor from media import removeMetaData diff --git a/utils.py b/utils.py index 29b89fc6..c8a235ec 100644 --- a/utils.py +++ b/utils.py @@ -190,3 +190,13 @@ def deletePost(baseDir: str,httpPrefix: str,nickname: str,domain: str,postFilena os.remove(repliesFilename) # finally, remove the post itself os.remove(postFilename) + +def validNickname(nickname: str) -> bool: + forbiddenChars=['.',' ','/','?',':',';','@'] + for c in forbiddenChars: + if c in nickname: + return False + reservedNames=['inbox','outbox','following','followers','capabilities'] + if nickname in reservedNames: + return False + return True