baseDir as parameter

master
Bob Mottram 2019-06-30 23:56:37 +01:00
parent bee4f185a2
commit eeb74f6e16
7 changed files with 86 additions and 64 deletions

View File

@ -69,7 +69,7 @@ class PubServer(BaseHTTPRequestHandler):
self.wfile.write(wfResult.encode('utf-8'))
return
wfResult=webfingerLookup(self.path)
wfResult=webfingerLookup(self.path,self.server.baseDir)
if wfResult:
self._set_headers('application/json')
self.wfile.write(json.dumps(wfResult).encode('utf-8'))
@ -103,7 +103,7 @@ class PubServer(BaseHTTPRequestHandler):
self.GETbusy=False
return
# get outbox feed for a person
outboxFeed=personOutboxJson(self.server.domain,self.server.port,self.path,self.server.https,maxPostsInFeed)
outboxFeed=personOutboxJson(self.server.baseDir,self.server.domain,self.server.port,self.path,self.server.https,maxPostsInFeed)
if outboxFeed:
self._set_headers('application/json')
self.wfile.write(json.dumps(outboxFeed).encode('utf-8'))
@ -122,13 +122,13 @@ class PubServer(BaseHTTPRequestHandler):
self.GETbusy=False
return
# look up a person
getPerson = personLookup(self.server.domain,self.path)
getPerson = personLookup(self.server.domain,self.path,self.server.baseDir)
if getPerson:
self._set_headers('application/json')
self.wfile.write(json.dumps(getPerson).encode('utf-8'))
self.GETbusy=False
return
personKey = personKeyLookup(self.server.domain,self.path)
personKey = personKeyLookup(self.server.domain,self.path,self.server.baseDir)
if personKey:
self._set_headers('text/html; charset=utf-8')
self.wfile.write(personKey.encode('utf-8'))
@ -140,8 +140,7 @@ class PubServer(BaseHTTPRequestHandler):
self.GETbusy=False
return
# check that the file exists
baseDir=os.getcwd()
filename=baseDir+self.path
filename=self.server.baseDir+self.path
if os.path.isfile(filename):
self._set_headers('application/json')
with open(filename, 'r', encoding='utf8') as File:
@ -208,5 +207,6 @@ def runDaemon(domain: str,port=80,https=True,fedList=[],useTor=False) -> None:
httpd.port=port
httpd.https=https
httpd.federationList=fedList.copy()
httpd.baseDir=os.getcwd()
print('Running ActivityPub daemon on ' + domain + ' port ' + str(port))
httpd.serve_forever()

View File

@ -19,6 +19,7 @@ from posts import sendPost
from session import createSession
from session import getJson
import json
import os
import sys
import requests
from pprint import pprint
@ -43,8 +44,10 @@ domain='127.0.0.1'
port=6227
https=False
useTor=False
baseDir=os.getcwd()
session = createSession(useTor)
clearFollows(username,domain)
followPerson(username,domain,'badger','wild.com',federationList)
followPerson(username,domain,'squirrel','secret.com',federationList)
@ -70,17 +73,17 @@ followerOfPerson(username,domain,'giraffe','trees.com',federationList)
#sys.exit()
privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(username,domain,port,https,True)
privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(baseDir,username,domain,port,https,True)
#deleteAllPosts(username,domain)
setPreferredUsername(username,domain,'badger')
setBio(username,domain,'Some personal info')
#createPublicPost(username, domain, https, "G'day world!", False, True, None, None, 'Not suitable for Vogons')
#archivePosts(username,domain,4)
#outboxJson=createOutbox(username,domain,port,https,2,True,None)
#archivePosts(username,domain,baseDir,4)
#outboxJson=createOutbox(baseDir,username,domain,port,https,2,True,None)
#pprint(outboxJson)
testPostMessageBetweenServers()
#runDaemon(domain,port,https,federationList,useTor)
#testPostMessageBetweenServers()
runDaemon(domain,port,https,federationList,useTor)
#testHttpsig()
sys.exit()

View File

@ -53,7 +53,7 @@ def validPublishedDate(published):
return False
return True
def receiveMessage(message):
def receiveMessage(message,baseDir: str):
if not message.get('type'):
return
if message['type']!='Create':
@ -76,7 +76,6 @@ def receiveMessage(message):
domain=''
messageId=message['id'].replace('/','_')
handle=username.lower()+'@'+domain.lower()
baseDir=os.getcwd()
if not os.path.isdir(baseDir+'/accounts/'+handle):
os.mkdir(baseDir+'/accounts/'+handle)
if not os.path.isdir(baseDir+'/accounts/'+handle+'/inbox'):

View File

@ -21,7 +21,7 @@ def generateRSAKey() -> (str,str):
publicKeyPem = key.publickey().exportKey("PEM").decode("utf-8")
return privateKeyPem,publicKeyPem
def createPerson(username: str,domain: str,port: int,https: bool, saveToFile: bool) -> (str,str,{},{}):
def createPerson(baseDir: str,username: str,domain: str,port: int,https: bool, saveToFile: bool) -> (str,str,{},{}):
"""Returns the private key, public key, actor and webfinger endpoint
"""
prefix='https'
@ -31,7 +31,7 @@ def createPerson(username: str,domain: str,port: int,https: bool, saveToFile: bo
privateKeyPem,publicKeyPem=generateRSAKey()
webfingerEndpoint=createWebfingerEndpoint(username,domain,port,https,publicKeyPem)
if saveToFile:
storeWebfingerEndpoint(username,domain,webfingerEndpoint)
storeWebfingerEndpoint(username,domain,baseDir,webfingerEndpoint)
handle=username.lower()+'@'+domain.lower()
if port!=80 and port!=443:
@ -79,7 +79,6 @@ def createPerson(username: str,domain: str,port: int,https: bool, saveToFile: bo
if saveToFile:
# save person to file
baseDir=os.getcwd()
peopleSubdir='/accounts'
if not os.path.isdir(baseDir+peopleSubdir):
os.mkdir(baseDir+peopleSubdir)
@ -114,7 +113,7 @@ def validUsername(username):
return False
return True
def personKeyLookup(domain: str,path: str) -> str:
def personKeyLookup(domain: str,path: str,baseDir: str) -> str:
"""Lookup the public key of the person with a given username
"""
if not path.endswith('/main-key'):
@ -125,7 +124,6 @@ def personKeyLookup(domain: str,path: str) -> str:
if not validUsername(username):
return None
handle=username.lower()+'@'+domain.lower()
baseDir=os.getcwd()
filename=baseDir+'/accounts/'+handle.lower()+'.json'
if not os.path.isfile(filename):
return None
@ -137,7 +135,7 @@ def personKeyLookup(domain: str,path: str) -> str:
return personJson['publicKey']['publicKeyPem']
return None
def personLookup(domain: str,path: str) -> {}:
def personLookup(domain: str,path: str,baseDir: str) -> {}:
"""Lookup the person for an given username
"""
notPersonLookup=['/inbox','/outbox','/outboxarchive','/followers','/following','/featured','.png','.jpg','.gif','.mpv','#main-key','/main-key']
@ -154,7 +152,6 @@ def personLookup(domain: str,path: str) -> {}:
if not validUsername(username):
return None
handle=username.lower()+'@'+domain.lower()
baseDir=os.getcwd()
filename=baseDir+'/accounts/'+handle.lower()+'.json'
if not os.path.isfile(filename):
return None
@ -163,7 +160,7 @@ def personLookup(domain: str,path: str) -> {}:
personJson=commentjson.load(fp)
return personJson
def personOutboxJson(domain: str,port: int,path: str,https: bool,noOfItems: int) -> []:
def personOutboxJson(baseDir: str,domain: str,port: int,path: str,https: bool,noOfItems: int) -> []:
"""Obtain the outbox feed for the given person
"""
if not '/outbox' in path:
@ -197,7 +194,7 @@ def personOutboxJson(domain: str,port: int,path: str,https: bool,noOfItems: int)
return None
if not validUsername(username):
return None
return createOutbox(username,domain,port,https,noOfItems,headerOnly,pageNumber)
return createOutbox(baseDir,username,domain,port,https,noOfItems,headerOnly,pageNumber)
def setPreferredUsername(username: str, domain: str, preferredName: str) -> bool:
if len(preferredName)>32:

View File

@ -23,22 +23,16 @@ from pprint import pprint
from random import randint
from session import getJson
from session import postJson
from webfinger import webfingerHandle
try:
from BeautifulSoup import BeautifulSoup
except ImportError:
from bs4 import BeautifulSoup
# Contains threads for posts being sent
sendThreads = []
# stores the results from recent post sending attempts
postLog = []
def getPersonKey(username: str,domain: str,keyType='public'):
def getPersonKey(username: str,domain: str,baseDir: str,keyType='public'):
"""Returns the public or private key of a person
"""
handle=username+'@'+domain
baseDir=os.getcwd()
keyFilename=baseDir+'/keys/'+keyType+'/'+handle.lower()+'.key'
if not os.path.isfile(keyFilename):
return ''
@ -200,11 +194,10 @@ def getUserPosts(session,wfRequest,maxPosts,maxMentions,maxEmoji,maxAttachments,
break
return userPosts
def createOutboxDir(username: str,domain: str) -> str:
def createOutboxDir(username: str,domain: str,baseDir: str) -> str:
"""Create an outbox for a person and returns the feed filename and directory
"""
handle=username.lower()+'@'+domain.lower()
baseDir=os.getcwd()
if not os.path.isdir(baseDir+'/accounts/'+handle):
os.mkdir(baseDir+'/accounts/'+handle)
outboxDir=baseDir+'/accounts/'+handle+'/outbox'
@ -212,11 +205,10 @@ def createOutboxDir(username: str,domain: str) -> str:
os.mkdir(outboxDir)
return outboxDir
def createOutboxArchive(username: str,domain: str) -> str:
def createOutboxArchive(username: str,domain: str,baseDir: str) -> str:
"""Creates an archive directory for outbox posts
"""
handle=username.lower()+'@'+domain.lower()
baseDir=os.getcwd()
if not os.path.isdir(baseDir+'/accounts/'+handle):
os.mkdir(baseDir+'/accounts/'+handle)
outboxArchiveDir=baseDir+'/accounts/'+handle+'/outboxarchive'
@ -224,10 +216,10 @@ def createOutboxArchive(username: str,domain: str) -> str:
os.mkdir(outboxArchiveDir)
return outboxArchiveDir
def deleteAllPosts(username: str, domain: str) -> None:
def deleteAllPosts(username: str, domain: str,baseDir: str) -> None:
"""Deletes all posts for a person
"""
outboxDir = createOutboxDir(username,domain)
outboxDir = createOutboxDir(username,domain,baseDir)
for deleteFilename in os.listdir(outboxDir):
filePath = os.path.join(outboxDir, deleteFilename)
try:
@ -248,7 +240,7 @@ def getStatusNumber() -> (str,str):
conversationDate=currTime.strftime("%Y-%m-%d")
return statusNumber,published
def createPostBase(username: str, domain: str, toUrl: str, ccUrl: str, https: bool, content: str, followersOnly: bool, saveToFile: bool, inReplyTo=None, inReplyToAtomUri=None, subject=None) -> {}:
def createPostBase(baseDir: str,username: str, domain: str, toUrl: str, ccUrl: str, https: bool, content: str, followersOnly: bool, saveToFile: bool, inReplyTo=None, inReplyToAtomUri=None, subject=None) -> {}:
"""Creates a public post
"""
prefix='https'
@ -306,7 +298,7 @@ def createPostBase(username: str, domain: str, toUrl: str, ccUrl: str, https: bo
}
}
if saveToFile:
outboxDir = createOutboxDir(username,domain)
outboxDir = createOutboxDir(username,domain,baseDir)
filename=outboxDir+'/'+newPostId.replace('/','#')+'.json'
with open(filename, 'w') as fp:
commentjson.dump(newPost, fp, indent=4, sort_keys=False)
@ -320,7 +312,7 @@ def createPublicPost(username: str, domain: str, https: bool, content: str, foll
prefix='http'
return createPostBase(username, domain, 'https://www.w3.org/ns/activitystreams#Public', prefix+'://'+domain+'/users/'+username+'/followers', https, content, followersOnly, saveToFile, inReplyTo, inReplyToAtomUri, subject)
def threadSendPost(session,postJsonObject,federationList,inboxUrl: str,signatureHeader) -> None:
def threadSendPost(session,postJsonObject,federationList,inboxUrl: str,baseDir: str,signatureHeader,postLog) -> None:
"""Sends a post with exponential backoff
"""
tries=0
@ -334,7 +326,6 @@ def threadSendPost(session,postJsonObject,federationList,inboxUrl: str,signature
while len(postLog)>64:
postlog.pop(0)
# save the log file
baseDir=os.getcwd()
filename=baseDir+'/post.log'
with open(filename, "w") as logFile:
for line in postLog:
@ -344,19 +335,24 @@ def threadSendPost(session,postJsonObject,federationList,inboxUrl: str,signature
time.sleep(backoffTime)
backoffTime *= 2
def sendPost(session,username: str, domain: str, toUsername: str, toDomain: str, cc: str, https: bool, content: str, followersOnly: bool, saveToFile: bool, federationList, inReplyTo=None, inReplyToAtomUri=None, subject=None) -> int:
def sendPost(session,baseDir,username: str, domain: str, port: int, toUsername: str, toDomain: str, toPort: int, cc: str, https: bool, content: str, followersOnly: bool, saveToFile: bool, federationList, sendThreads, postLog, inReplyTo=None, inReplyToAtomUri=None, subject=None) -> int:
"""Post to another inbox
"""
prefix='https'
if not https:
prefix='http'
# lookup the inbox
handle=prefix+'://'+domain+'/@'+username
wfRequest = webfingerHandle(session,handle,True)
if toPort!=80 and toPort!=443:
toDomain=toDomain+':'+str(toPort)
handle=prefix+'://'+toDomain+'/@'+toUsername
# lookup the inbox for the To handle
wfRequest = webfingerHandle(session,handle,https)
if not wfRequest:
return 1
# get the actor inbox for the To handle
inboxUrl,pubKey,toPersonId = getPersonBox(session,wfRequest,'inbox')
if not inboxUrl:
return 2
@ -367,7 +363,8 @@ def sendPost(session,username: str, domain: str, toUsername: str, toDomain: str,
postJsonObject=createPostBase(username, domain, toPersonId, cc, https, content, followersOnly, saveToFile, inReplyTo, inReplyToAtomUri, subject)
privateKeyPem=getPersonKey(username,domain,'private')
# get the senders private key
privateKeyPem=getPersonKey(username,domain,baseDir,'private')
if len(privateKeyPem)==0:
return 5
@ -379,19 +376,19 @@ def sendPost(session,username: str, domain: str, toUsername: str, toDomain: str,
while len(sendThreads)>10:
sendThreads[0].kill()
sendThreads.pop(0)
thr = threadWithTrace(target=threadSendPost,args=(session,postJsonObject.copy(),federationList,inboxUrl,signatureHeader.copy()),daemon=True)
thr = threadWithTrace(target=threadSendPost,args=(session,postJsonObject.copy(),federationList,inboxUrl,baseDir,signatureHeader.copy(),postLog),daemon=True)
sendThreads.append(thr)
thr.start()
return 0
def createOutbox(username: str,domain: str,port: int,https: bool,itemsPerPage: int,headerOnly: bool,pageNumber=None) -> {}:
def createOutbox(baseDir: str,username: str,domain: str,port: int,https: bool,itemsPerPage: int,headerOnly: bool,pageNumber=None) -> {}:
"""Constructs the outbox feed
"""
prefix='https'
if not https:
prefix='http'
outboxDir = createOutboxDir(username,domain)
outboxDir = createOutboxDir(username,domain,baseDir)
if port!=80 and port!=443:
domain = domain+':'+str(port)
@ -482,12 +479,12 @@ def createOutbox(username: str,domain: str,port: int,https: bool,itemsPerPage: i
return outboxHeader
return outboxItems
def archivePosts(username: str,domain: str,maxPostsInOutbox=256) -> None:
def archivePosts(username: str,domain: str,baseDir: str,maxPostsInOutbox=256) -> None:
"""Retain a maximum number of posts within the outbox
Move any others to an archive directory
"""
outboxDir = createOutboxDir(username,domain)
archiveDir = createOutboxArchive(username,domain)
outboxDir = createOutboxDir(username,domain,baseDir)
archiveDir = createOutboxArchive(username,domain,baseDir)
postsInOutbox=sorted(os.listdir(outboxDir), reverse=False)
noOfPosts=len(postsInOutbox)
if noOfPosts<=maxPostsInOutbox:

View File

@ -22,6 +22,7 @@ from session import createSession
from person import createPerson
from posts import deleteAllPosts
from posts import createPublicPost
from posts import sendPost
from follow import followPerson
from follow import followerOfPerson
@ -34,7 +35,8 @@ def testHttpsigBase(withDigest):
domain='argumentative.social'
https=True
port=80
privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(username,domain,port,https,False)
baseDir=os.getcwd()
privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(baseDir,username,domain,port,https,False)
messageBodyJson = '{"a key": "a value", "another key": "A string"}'
if not withDigest:
headers = {'host': domain}
@ -96,9 +98,8 @@ def createServerAlice(path: str,port: int):
domain='127.0.0.1'
https=False
useTor=False
session = createSession(useTor)
privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(username,domain,port,https,True)
deleteAllPosts(username,domain)
privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(path,username,domain,port,https,True)
deleteAllPosts(username,domain,path)
followPerson(username,domain,'bob','127.0.0.1:61936',federationList)
followerOfPerson(username,domain,'bob','127.0.0.1:61936',federationList)
createPublicPost(username, domain, https, "No wise fish would go anywhere without a porpoise", False, True)
@ -120,9 +121,8 @@ def createServerBob(path: str,port: int):
domain='127.0.0.1'
https=False
useTor=False
session = createSession(useTor)
privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(username,domain,port,https,True)
deleteAllPosts(username,domain)
privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(path,username,domain,port,https,True)
deleteAllPosts(username,domain,path)
followPerson(username,domain,'alice','127.0.0.1:61935',federationList)
followerOfPerson(username,domain,'alice','127.0.0.1:61935',federationList)
createPublicPost(username, domain, https, "It's your life, live it your way.", False, True)
@ -141,16 +141,24 @@ def testPostMessageBetweenServers():
testServerAliceRunning = False
testServerBobRunning = False
https=False
useTor=False
federationList=['127.0.0.1']
baseDir=os.getcwd()
if not os.path.isdir(baseDir+'/.tests'):
os.mkdir(baseDir+'/.tests')
# create the servers
thrAlice = threadWithTrace(target=createServerAlice,args=(baseDir+'/.tests/alice',61935),daemon=True)
aliceDir=baseDir+'/.tests/alice'
alicePort=61935
thrAlice = threadWithTrace(target=createServerAlice,args=(aliceDir,alicePort),daemon=True)
thrAlice.start()
assert thrAlice.isAlive()==True
thrBob = threadWithTrace(target=createServerBob,args=(baseDir+'/.tests/bob',61936),daemon=True)
bobDir=baseDir+'/.tests/bob'
bobPort=61936
thrBob = threadWithTrace(target=createServerBob,args=(bobDir,bobPort),daemon=True)
thrBob.start()
assert thrBob.isAlive()==True
@ -160,6 +168,19 @@ def testPostMessageBetweenServers():
time.sleep(3)
print('Alice sends to Bob')
os.chdir(aliceDir)
sessionAlice = createSession(useTor)
inReplyTo=None
inReplyToAtomUri=None
subject=None
aliceSendThreads = []
alicePostLog = []
sendResult = sendPost(sessionAlice,aliceDir,'alice', '127.0.0.1', alicePort, 'bob', '127.0.0.1', bobPort, '', https, 'Why is a mouse when it spins?', False, True, federationList, aliceSendThreads, alicePostLog, inReplyTo, inReplyToAtomUri, subject)
print('sendResult: '+str(sendResult))
time.sleep(3)
# stop the servers
thrAlice.kill()
thrAlice.join()

View File

@ -45,9 +45,13 @@ def webfingerHandle(session,handle: str,https: bool):
if not https:
prefix='http'
url = '{}://{}/.well-known/webfinger'.format(prefix,domain)
if ':' in domain:
domain=domain.split(':')[0]
par = {'resource': 'acct:{}'.format(username+'@'+domain)}
hdr = {'Accept': 'application/jrd+json'}
#try:
print("webfinger url = "+url)
print("webfinger par = "+str(par))
result = getJson(session, url, hdr, par)
#except:
# print("Unable to webfinger " + url)
@ -64,11 +68,10 @@ def generateMagicKey(publicKeyPem):
pubexp = base64.urlsafe_b64encode(number.long_to_bytes(privkey.e)).decode("utf-8")
return f"data:application/magic-public-key,RSA.{mod}.{pubexp}"
def storeWebfingerEndpoint(username: str,domain: str,wfJson) -> bool:
def storeWebfingerEndpoint(username: str,domain: str,baseDir: str,wfJson) -> bool:
"""Stores webfinger endpoint for a user to a file
"""
handle=username+'@'+domain
baseDir=os.getcwd()
wfSubdir='/wfendpoints'
if not os.path.isdir(baseDir+wfSubdir):
os.mkdir(baseDir+wfSubdir)
@ -140,25 +143,27 @@ def webfingerMeta() -> str:
" </Link>" \
"</XRD>"
def webfingerLookup(path: str):
def webfingerLookup(path: str,baseDir: str):
"""Lookup the webfinger endpoint for an account
"""
if not path.startswith('/.well-known/webfinger?'):
return None
handle=None
print('************** '+path)
if 'resource=acct:' in path:
handle=path.split('resource=acct:')[1].strip()
else:
if 'resource=acct%3A' in path:
handle=path.split('resource=acct%3A')[1].replace('%40','@').strip()
print('************** handle: '+handle)
if not handle:
return None
if '&' in handle:
handle=handle.split('&')[0].strip()
if '@' not in handle:
return None
baseDir=os.getcwd()
filename=baseDir+'/wfendpoints/'+handle.lower()+'.json'
print('************** filename: '+filename)
if not os.path.isfile(filename):
return None
wfJson={"user": "unknown"}