diff --git a/daemon.py b/daemon.py
index f3c5673a..950ab1f6 100644
--- a/daemon.py
+++ b/daemon.py
@@ -6,7 +6,7 @@ __maintainer__ = "Bob Mottram"
__email__ = "bob@freedombone.net"
__status__ = "Production"
-from http.server import BaseHTTPRequestHandler, HTTPServer
+from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
#import socketserver
import json
import time
@@ -14,6 +14,7 @@ from pprint import pprint
from session import createSession
from webfinger import webfingerMeta
from webfinger import webfingerLookup
+from webfinger import webfingerHandle
from person import personLookup
from person import personKeyLookup
from person import personOutboxJson
@@ -60,9 +61,11 @@ class PubServer(BaseHTTPRequestHandler):
self.wfile.write("
404 Not Found
".encode('utf-8'))
def _webfinger(self) -> bool:
+ print('############### _webfinger well-known')
if not self.path.startswith('/.well-known'):
return False
+ print('############### _webfinger host-meta')
if self.path.startswith('/.well-known/host-meta'):
wfResult=webfingerMeta()
if wfResult:
@@ -70,11 +73,13 @@ class PubServer(BaseHTTPRequestHandler):
self.wfile.write(wfResult.encode('utf-8'))
return
+ print('############### _webfinger lookup '+self.path+' '+str(self.server.baseDir))
wfResult=webfingerLookup(self.path,self.server.baseDir)
if wfResult:
- self._set_headers('application/json')
+ self._set_headers('application/jrd+json')
self.wfile.write(json.dumps(wfResult).encode('utf-8'))
else:
+ print('############### _webfinger lookup 404 '+self.path)
self._404()
return True
@@ -85,24 +90,30 @@ class PubServer(BaseHTTPRequestHandler):
return False
return True
- def do_GET(self):
+ def do_GET(self):
+ print('############### GET from '+self.server.baseDir)
if self.server.GETbusy:
currTimeGET=int(time.time())
if currTimeGET-self.server.lastGET<10:
+ print('############### Busy')
self.send_response(429)
self.end_headers()
return
self.server.lastGET=currTimeGET
self.server.GETbusy=True
+ print('############### _permittedDir')
if not self._permittedDir(self.path):
+ print('############# Not permitted')
self._404()
self.server.GETbusy=False
return
# get webfinger endpoint for a person
+ print('############### _webfinger')
if self._webfinger():
self.server.GETbusy=False
return
+ print('############### _webfinger end')
# get outbox feed for a person
outboxFeed=personOutboxJson(self.server.baseDir,self.server.domain,self.server.port,self.path,self.server.https,maxPostsInFeed)
if outboxFeed:
@@ -137,6 +148,7 @@ class PubServer(BaseHTTPRequestHandler):
return
# check that a json file was requested
if not self.path.endswith('.json'):
+ print('############# Not json: '+self.path+' '+self.server.baseDir)
self._404()
self.server.GETbusy=False
return
@@ -149,12 +161,13 @@ class PubServer(BaseHTTPRequestHandler):
contentJson=json.loads(content)
self.wfile.write(json.dumps(contentJson).encode('utf8'))
else:
+ print('############# Unknown file')
self._404()
self.server.GETbusy=False
def do_HEAD(self):
self._set_headers('application/json')
-
+
def do_POST(self):
if self.server.POSTbusy:
currTimePOST=int(time.time())
@@ -200,24 +213,16 @@ class PubServer(BaseHTTPRequestHandler):
self.server.POSTbusy=False
return
-
- print('**************** POST get handle')
- handle=''
+ pprint(messageJson)
+ print('**************** POST get actor url from '+self.server.baseDir)
+ personUrl=messageJson['object']['attributedTo']
print('**************** POST create session')
currSessionTime=int(time.time())
if currSessionTime-self.server.sessionLastUpdate>600:
self.server.sessionLastUpdate=currSessionTime
self.server.session = createSession(self.server.useTor)
- print('**************** POST webfinger the handle')
- wfRequest = webfingerHandle(self.server.session,handle,self.server.https,self.server.cachedWebfingers)
- if not wfRequest:
- print('**************** POST unknown webfinger')
- self.send_response(401)
- self.end_headers()
- self.server.POSTbusy=False
- return
- print('**************** POST get public key')
- pubKey=getPersonPubKey(self.server.session,wfRequest,self.server.personCache)
+ print('**************** POST get public key of '+personUrl+' from '+self.server.baseDir)
+ pubKey=getPersonPubKey(self.server.session,personUrl,self.server.personCache)
if not pubKey:
print('**************** POST no sender public key')
self.send_response(401)
@@ -233,6 +238,8 @@ class PubServer(BaseHTTPRequestHandler):
# send the message back
#self._set_headers('application/json')
#self.wfile.write(json.dumps(message).encode('utf-8'))
+
+ self.server.receivedMessage=True
self.send_response(200)
self.end_headers()
self.server.POSTbusy=False
@@ -244,8 +251,8 @@ def runDaemon(domain: str,port=80,https=True,fedList=[],useTor=False) -> None:
print('Invalid domain: ' + domain)
return
- serverAddress = ('', port)
- httpd = HTTPServer(serverAddress, PubServer)
+ serverAddress = (domain, port)
+ httpd = ThreadingHTTPServer(serverAddress, PubServer)
httpd.domain=domain
httpd.port=port
httpd.https=https
@@ -260,5 +267,6 @@ def runDaemon(domain: str,port=80,https=True,fedList=[],useTor=False) -> None:
httpd.lastPOST=0
httpd.GETbusy=False
httpd.POSTbusy=False
+ httpd.receivedMessage=False
print('Running ActivityPub daemon on ' + domain + ' port ' + str(port))
httpd.serve_forever()
diff --git a/person.py b/person.py
index 53e9554f..f745a3b5 100644
--- a/person.py
+++ b/person.py
@@ -123,6 +123,8 @@ def personKeyLookup(domain: str,path: str,baseDir: str) -> str:
username=path.replace('/users/','',1).replace('/main-key','')
if not validUsername(username):
return None
+ if ':' in domain:
+ domain=domain.split(':')[0]
handle=username.lower()+'@'+domain.lower()
filename=baseDir+'/accounts/'+handle.lower()+'.json'
if not os.path.isfile(filename):
@@ -151,6 +153,8 @@ def personLookup(domain: str,path: str,baseDir: str) -> {}:
return None
if not validUsername(username):
return None
+ if ':' in domain:
+ domain=domain.split(':')[0]
handle=username.lower()+'@'+domain.lower()
filename=baseDir+'/accounts/'+handle.lower()+'.json'
if not os.path.isfile(filename):
diff --git a/posts.py b/posts.py
index 1b8b3340..ffcc402b 100644
--- a/posts.py
+++ b/posts.py
@@ -107,9 +107,8 @@ def getPersonBox(session,wfRequest: {},personCache: {},boxName='inbox') -> (str,
return personJson[boxName],pubKeyId,pubKey,personId
-def getPersonPubKey(session,wfRequest,personCache: {}) -> str:
+def getPersonPubKey(session,personUrl: str,personCache: {}) -> str:
asHeader = {'Accept': 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'}
- personUrl = getUserUrl(wfRequest)
if not personUrl:
return None
personJson = getPersonFromCache(personUrl,personCache)
@@ -121,7 +120,6 @@ def getPersonPubKey(session,wfRequest,personCache: {}) -> str:
pubKey=personJson['publicKey']['publicKeyPem']
storePersonInCache(personUrl,personJson,personCache)
-
return pubKey
def getUserPosts(session,wfRequest: {},maxPosts: int,maxMentions: int,maxEmoji: int,maxAttachments: int,federationList: [],personCache: {}) -> {}:
diff --git a/session.py b/session.py
index 0ffcc1ff..10371ff3 100644
--- a/session.py
+++ b/session.py
@@ -26,9 +26,11 @@ def getJson(session,url: str,headers: {},params: {}) -> {}:
sessionHeaders=headers
if params:
sessionParams=params
- sessionHeaders['User-agent'] = "HotJava/1.1.2 FCS"
+ sessionHeaders['User-agent'] = "Mozilla/5.0 (Linux; Android 5.1.1; Nexus 5 Build/LMY48B; wv)"
session.cookies.clear()
- return session.get(url, headers=sessionHeaders, params=sessionParams).json()
+ result=session.get(url, headers=sessionHeaders, params=sessionParams)
+ print("*****result "+url+' ' + str(result))
+ return result.json()
def postJson(session,postJsonObject: {},federationList: [],inboxUrl: str,headers: {}) -> str:
"""Post a json message to the inbox of another person
diff --git a/tests.py b/tests.py
index c0533a80..131eebfa 100644
--- a/tests.py
+++ b/tests.py
@@ -98,21 +98,19 @@ def testThreads():
thr.join()
assert thr.isAlive()==False
-def createServerAlice(path: str,port: int):
+def createServerAlice(path: str,domain: str,port: int,federationList: []):
print('Creating test server: Alice on port '+str(port))
if os.path.isdir(path):
shutil.rmtree(path)
os.mkdir(path)
os.chdir(path)
- federationList=['127.0.0.1']
username='alice'
- domain='127.0.0.1'
https=False
useTor=False
privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(path,username,domain,port,https,True)
deleteAllPosts(username,domain,path)
- followPerson(path,username,domain,'bob','127.0.0.1:61936',federationList)
- followerOfPerson(path,username,domain,'bob','127.0.0.1:61936',federationList)
+ followPerson(path,username,domain,'bob','127.0.10.2:61936',federationList)
+ followerOfPerson(path,username,domain,'bob','127.0.10.2:61936',federationList)
createPublicPost(path,username, domain, port,https, "No wise fish would go anywhere without a porpoise", False, True)
createPublicPost(path,username, domain, port,https, "Curiouser and curiouser!", False, True)
createPublicPost(path,username, domain, port,https, "In the gardens of memory, in the palace of dreams, that is where you and I shall meet", False, True)
@@ -121,21 +119,19 @@ def createServerAlice(path: str,port: int):
print('Server running: Alice')
runDaemon(domain,port,https,federationList,useTor)
-def createServerBob(path: str,port: int):
+def createServerBob(path: str,domain: str,port: int,federationList: []):
print('Creating test server: Bob on port '+str(port))
if os.path.isdir(path):
shutil.rmtree(path)
os.mkdir(path)
os.chdir(path)
- federationList=['127.0.0.1']
username='bob'
- domain='127.0.0.1'
https=False
useTor=False
privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(path,username,domain,port,https,True)
deleteAllPosts(username,domain,path)
- followPerson(path,username,domain,'alice','127.0.0.1:61935',federationList)
- followerOfPerson(path,username,domain,'alice','127.0.0.1:61935',federationList)
+ followPerson(path,username,domain,'alice','127.0.10.1:61935',federationList)
+ followerOfPerson(path,username,domain,'alice','127.0.10.1:61935',federationList)
createPublicPost(path,username, domain, port,https, "It's your life, live it your way.", False, True)
createPublicPost(path,username, domain, port,https, "One of the things I've realised is that I am very simple", False, True)
createPublicPost(path,username, domain, port,https, "Quantum physics is a bit of a passion of mine", False, True)
@@ -154,7 +150,7 @@ def testPostMessageBetweenServers():
https=False
useTor=False
- federationList=['127.0.0.1']
+ federationList=['127.0.0.1','127.0.10.1','127.0.10.2']
baseDir=os.getcwd()
if not os.path.isdir(baseDir+'/.tests'):
@@ -162,22 +158,25 @@ def testPostMessageBetweenServers():
# create the servers
aliceDir=baseDir+'/.tests/alice'
+ aliceDomain='127.0.10.1'
alicePort=61935
- thrAlice = threadWithTrace(target=createServerAlice,args=(aliceDir,alicePort),daemon=True)
- thrAlice.start()
- assert thrAlice.isAlive()==True
+ thrAlice = threadWithTrace(target=createServerAlice,args=(aliceDir,aliceDomain,alicePort,federationList),daemon=True)
bobDir=baseDir+'/.tests/bob'
+ bobDomain='127.0.10.2'
bobPort=61936
- thrBob = threadWithTrace(target=createServerBob,args=(bobDir,bobPort),daemon=True)
+ thrBob = threadWithTrace(target=createServerBob,args=(bobDir,bobDomain,bobPort,federationList),daemon=True)
+
+ thrAlice.start()
thrBob.start()
+ assert thrAlice.isAlive()==True
assert thrBob.isAlive()==True
# wait for both servers to be running
while not (testServerAliceRunning and testServerBobRunning):
time.sleep(1)
- time.sleep(5)
+ time.sleep(6)
print('Alice sends to Bob')
os.chdir(aliceDir)
@@ -192,10 +191,11 @@ def testPostMessageBetweenServers():
ccUrl=None
alicePersonCache={}
aliceCachedWebfingers={}
- sendResult = sendPost(sessionAlice,aliceDir,'alice', '127.0.0.1', alicePort, 'bob', '127.0.0.1', bobPort, ccUrl, https, 'Why is a mouse when it spins?', followersOnly, saveToFile, federationList, aliceSendThreads, alicePostLog, aliceCachedWebfingers,alicePersonCache,inReplyTo, inReplyToAtomUri, subject)
+ sendResult = sendPost(sessionAlice,aliceDir,'alice', aliceDomain, alicePort, 'bob', bobDomain, bobPort, ccUrl, https, 'Why is a mouse when it spins?', followersOnly, saveToFile, federationList, aliceSendThreads, alicePostLog, aliceCachedWebfingers,alicePersonCache,inReplyTo, inReplyToAtomUri, subject)
print('sendResult: '+str(sendResult))
- time.sleep(15)
+ for i in range(10):
+ time.sleep(1)
# stop the servers
thrAlice.kill()
diff --git a/webfinger.py b/webfinger.py
index 08b49144..9402c5c5 100644
--- a/webfinger.py
+++ b/webfinger.py
@@ -38,23 +38,24 @@ def webfingerHandle(session,handle: str,https: bool,cachedWebfingers: {}) -> {}:
username, domain = parseHandle(handle)
if not username:
return None
- wf=getWebfingerFromCache(username+'@'+domain,cachedWebfingers)
+ wfDomain=domain
+ if ':' in wfDomain:
+ wfDomain=wfDomain.split(':')[0]
+ print('***********cachedWebfingers '+str(cachedWebfingers))
+ wf=getWebfingerFromCache(username+'@'+wfDomain,cachedWebfingers)
if wf:
return wf
prefix='https'
if not https:
- prefix='http'
+ prefix='http'
url = '{}://{}/.well-known/webfinger'.format(prefix,domain)
- if ':' in domain:
- domain=domain.split(':')[0]
- par = {'resource': 'acct:{}'.format(username+'@'+domain)}
+ par = {'resource': 'acct:{}'.format(username+'@'+wfDomain)}
hdr = {'Accept': 'application/jrd+json'}
#try:
result = getJson(session, url, hdr, par)
#except:
- # print("Unable to webfinger " + url)
- # return None
- storeWebfingerInCache(username+'@'+domain, result,cachedWebfingers)
+ # print("Unable to webfinger " + url + ' ' + str(hdr) + ' ' + str(par))
+ storeWebfingerInCache(username+'@'+wfDomain,result,cachedWebfingers)
return result
def generateMagicKey(publicKeyPem) -> str:
@@ -144,23 +145,33 @@ def webfingerMeta() -> str:
def webfingerLookup(path: str,baseDir: str) -> {}:
"""Lookup the webfinger endpoint for an account
"""
+ print('############### _webfinger lookup 1')
if not path.startswith('/.well-known/webfinger?'):
return None
+ print('############### _webfinger lookup 2')
handle=None
if 'resource=acct:' in path:
+ print('############### _webfinger lookup 3')
handle=path.split('resource=acct:')[1].strip()
else:
+ print('############### _webfinger lookup 4')
if 'resource=acct%3A' in path:
- handle=path.split('resource=acct%3A')[1].replace('%40','@').strip()
+ print('############### _webfinger lookup 5')
+ handle=path.split('resource=acct%3A')[1].replace('%40','@').strip()
+ print('############### _webfinger lookup 6')
if not handle:
return None
+ print('############### _webfinger lookup 7')
if '&' in handle:
handle=handle.split('&')[0].strip()
+ print('############### _webfinger lookup 8')
if '@' not in handle:
return None
filename=baseDir+'/wfendpoints/'+handle.lower()+'.json'
+ print('############### _webfinger lookup 9: '+filename)
if not os.path.isfile(filename):
return None
+ print('############### _webfinger lookup 10')
wfJson={"user": "unknown"}
with open(filename, 'r') as fp:
wfJson=commentjson.load(fp)