forked from indymedia/epicyon
Fixing c2s
parent
18629c88a8
commit
a98facaf33
50
daemon.py
50
daemon.py
|
@ -124,15 +124,17 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
messageJson= \
|
||||
outboxMessageCreateWrap(self.server.httpPrefix, \
|
||||
self.postToNickname, \
|
||||
self.server.domain,messageJson)
|
||||
self.server.domain, \
|
||||
self.server.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('atomUri') and \
|
||||
messageJson.get('to')):
|
||||
if self.server.debug:
|
||||
pprint(messageJson)
|
||||
print('DEBUG: POST to outbox - Create does not have the required parameters')
|
||||
return False
|
||||
# https://www.w3.org/TR/activitypub/#create-activity-outbox
|
||||
|
@ -150,23 +152,31 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
postId=messageJson['id']
|
||||
else:
|
||||
postId=None
|
||||
savePostToBox(self.server.baseDir,postId, \
|
||||
if self.server.debug:
|
||||
pprint(messageJson)
|
||||
savePostToBox(self.server.baseDir, \
|
||||
self.server.httpPrefix, \
|
||||
postId, \
|
||||
self.postToNickname, \
|
||||
self.server.domain,messageJson,'outbox')
|
||||
if not self.server.session:
|
||||
self.server.session= \
|
||||
createSession(self.server.domain,self.server.port,self.server.useTor)
|
||||
if self.server.debug:
|
||||
print('DEBUG: sending c2s post to followers')
|
||||
sendToFollowers(self.server.session,self.server.baseDir, \
|
||||
self.postToNickname,self.server.domain, \
|
||||
self.server.port, \
|
||||
self.server.httpPrefix, \
|
||||
self.server.federationList, \
|
||||
self.server.sendThreads, \
|
||||
self.server.postLog, \
|
||||
self.server.cachedWebfingers, \
|
||||
self.server.personCache, \
|
||||
messageJson,self.server.debug)
|
||||
self.postToNickname,self.server.domain, \
|
||||
self.server.port, \
|
||||
self.server.httpPrefix, \
|
||||
self.server.federationList, \
|
||||
self.server.sendThreads, \
|
||||
self.server.postLog, \
|
||||
self.server.cachedWebfingers, \
|
||||
self.server.personCache, \
|
||||
messageJson,self.server.debug)
|
||||
if self.server.debug:
|
||||
print('DEBUG: sending c2s post to named addresses')
|
||||
print('c2s sender: '+self.postToNickname+'@'+self.server.domain+':'+str(self.server.port))
|
||||
sendToNamedAddresses(self.server.session,self.server.baseDir, \
|
||||
self.postToNickname,self.server.domain, \
|
||||
self.server.port, \
|
||||
|
@ -580,7 +590,7 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
if '/users/' in self.path:
|
||||
if self._isAuthorized():
|
||||
self.outboxAuthenticated=True
|
||||
pathUsersSection=path.split('/users/')[1]
|
||||
pathUsersSection=self.path.split('/users/')[1]
|
||||
self.postToNickname=pathUsersSection.split('/')[0]
|
||||
if not self.outboxAuthenticated:
|
||||
self.send_response(405)
|
||||
|
@ -617,9 +627,16 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
|
||||
# https://www.w3.org/TR/activitypub/#object-without-create
|
||||
if self.outboxAuthenticated:
|
||||
if self._postToOutbox(messageJson):
|
||||
self.send_header('Location', \
|
||||
messageJson['object']['atomUri'])
|
||||
if self._postToOutbox(messageJson):
|
||||
if messageJson.get('object'):
|
||||
#self.send_header('Location', \
|
||||
self.headers['Location']= \
|
||||
messageJson['object']['id'].replace('/activity','')
|
||||
else:
|
||||
if messageJson.get('id'):
|
||||
#self.send_header('Location', \
|
||||
self.headers['Location']= \
|
||||
messageJson['id'].replace('/activity','')
|
||||
self.send_response(201)
|
||||
self.end_headers()
|
||||
self.server.POSTbusy=False
|
||||
|
@ -638,6 +655,7 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
self.path=='/sharedInbox':
|
||||
if not inboxMessageHasParams(messageJson):
|
||||
if self.server.debug:
|
||||
pprint(messageJson)
|
||||
print("DEBUG: inbox message doesn't have the required parameters")
|
||||
self.send_response(403)
|
||||
self.end_headers()
|
||||
|
|
|
@ -52,6 +52,7 @@ from follow import unfollowerOfPerson
|
|||
from follow import getFollowersOfPerson
|
||||
from tests import testPostMessageBetweenServers
|
||||
from tests import testFollowBetweenServers
|
||||
from tests import testClientToServer
|
||||
from tests import runAllTests
|
||||
from config import setConfigParam
|
||||
from config import getConfigParam
|
||||
|
@ -213,8 +214,9 @@ if args.tests:
|
|||
|
||||
if args.testsnetwork:
|
||||
print('Network Tests')
|
||||
testPostMessageBetweenServers()
|
||||
testFollowBetweenServers()
|
||||
testClientToServer()
|
||||
#testPostMessageBetweenServers()
|
||||
#testFollowBetweenServers()
|
||||
sys.exit()
|
||||
|
||||
if args.posts:
|
||||
|
|
8
media.py
8
media.py
|
@ -64,9 +64,10 @@ def attachImage(baseDir: str,httpPrefix: str,domain: str,port: int, \
|
|||
domain=domain+':'+str(port)
|
||||
|
||||
mPath=getMediaPath()
|
||||
createMediaDirs(baseDir,mPath)
|
||||
mediaPath=mPath+'/'+createPassword(32)+'.'+fileExtension
|
||||
mediaFilename=baseDir+'/'+mediaPath
|
||||
if baseDir:
|
||||
createMediaDirs(baseDir,mPath)
|
||||
mediaFilename=baseDir+'/'+mediaPath
|
||||
|
||||
attachmentJson={
|
||||
'mediaType': mediaType,
|
||||
|
@ -78,7 +79,8 @@ def attachImage(baseDir: str,httpPrefix: str,domain: str,port: int, \
|
|||
attachmentJson['blurhash']=getImageHash(imageFilename)
|
||||
postJson['attachment']=[attachmentJson]
|
||||
|
||||
copyfile(imageFilename,mediaFilename)
|
||||
if baseDir:
|
||||
copyfile(imageFilename,mediaFilename)
|
||||
|
||||
return postJson
|
||||
|
||||
|
|
117
posts.py
117
posts.py
|
@ -38,6 +38,7 @@ from capabilities import getOcapFilename
|
|||
from capabilities import capabilitiesUpdate
|
||||
from media import attachImage
|
||||
from content import addMentions
|
||||
from auth import createBasicAuthHeader
|
||||
try:
|
||||
from BeautifulSoup import BeautifulSoup
|
||||
except ImportError:
|
||||
|
@ -318,9 +319,10 @@ def createPostBase(baseDir: str,nickname: str, domain: str, port: int, \
|
|||
inReplyTo=None, inReplyToAtomUri=None, subject=None) -> {}:
|
||||
"""Creates a message
|
||||
"""
|
||||
# convert content to html
|
||||
content=addMentions(baseDir,httpPrefix, \
|
||||
nickname,domain,content)
|
||||
if not clientToServer:
|
||||
# convert content to html
|
||||
content=addMentions(baseDir,httpPrefix, \
|
||||
nickname,domain,content)
|
||||
|
||||
if port!=80 and port!=443:
|
||||
domain=domain+':'+str(port)
|
||||
|
@ -444,12 +446,16 @@ def createPostBase(baseDir: str,nickname: str, domain: str, port: int, \
|
|||
nickname,domain,newPost,'outbox')
|
||||
return newPost
|
||||
|
||||
def outboxMessageCreateWrap(httpPrefix: str,nickname: str,domain: str, \
|
||||
def outboxMessageCreateWrap(httpPrefix: str, \
|
||||
nickname: str,domain: str,port: int, \
|
||||
messageJson: {}) -> {}:
|
||||
"""Wraps a received message in a Create
|
||||
https://www.w3.org/TR/activitypub/#object-without-create
|
||||
"""
|
||||
|
||||
if port!=80 and port!=443:
|
||||
if ':' not in domain:
|
||||
domain=domain+':'+str(port)
|
||||
statusNumber,published = getStatusNumber()
|
||||
if messageJson.get('published'):
|
||||
published = messageJson['published']
|
||||
|
@ -458,7 +464,7 @@ def outboxMessageCreateWrap(httpPrefix: str,nickname: str,domain: str, \
|
|||
if messageJson.get('cc'):
|
||||
cc=messageJson['cc']
|
||||
# TODO
|
||||
capabilityUrl=''
|
||||
capabilityUrl=[]
|
||||
newPost = {
|
||||
'id': newPostId+'/activity',
|
||||
'capability': capabilityUrl,
|
||||
|
@ -605,7 +611,7 @@ def sendPost(session,baseDir: str,nickname: str, domain: str, port: int, \
|
|||
getPersonBox(session,wfRequest,personCache,postToBox)
|
||||
|
||||
# If there are more than one followers on the target domain
|
||||
# then send to teh shared inbox indead of the individual inbox
|
||||
# then send to the shared inbox indead of the individual inbox
|
||||
if nickname=='capabilities':
|
||||
inboxUrl=capabilityAcquisition
|
||||
if not capabilityAcquisition:
|
||||
|
@ -658,6 +664,76 @@ def sendPost(session,baseDir: str,nickname: str, domain: str, port: int, \
|
|||
thr.start()
|
||||
return 0
|
||||
|
||||
def sendPostViaServer(session,fromNickname: str,password: str, \
|
||||
fromDomain: str, fromPort: int, \
|
||||
toNickname: str, toDomain: str, toPort: int, cc: str, \
|
||||
httpPrefix: str, content: str, followersOnly: bool, \
|
||||
attachImageFilename: str,imageDescription: str,useBlurhash: bool, \
|
||||
cachedWebfingers: {},personCache: {}, \
|
||||
debug=False,inReplyTo=None,inReplyToAtomUri=None,subject=None) -> int:
|
||||
"""Send a post via a proxy (c2s)
|
||||
"""
|
||||
withDigest=True
|
||||
|
||||
if toPort!=80 and toPort!=443:
|
||||
if ':' not in fromDomain:
|
||||
fromDomain=fromDomain+':'+str(fromPort)
|
||||
|
||||
handle=httpPrefix+'://'+fromDomain+'/@'+fromNickname
|
||||
|
||||
# lookup the inbox for the To handle
|
||||
wfRequest = webfingerHandle(session,handle,httpPrefix,cachedWebfingers)
|
||||
if not wfRequest:
|
||||
if debug:
|
||||
print('DEBUG: webfinger failed for '+handle)
|
||||
return 1
|
||||
|
||||
postToBox='outbox'
|
||||
|
||||
# get the actor inbox for the To handle
|
||||
inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition = \
|
||||
getPersonBox(session,wfRequest,personCache,postToBox)
|
||||
|
||||
if not inboxUrl:
|
||||
if debug:
|
||||
print('DEBUG: No '+postToBox+' was found for '+handle)
|
||||
return 3
|
||||
if not fromPersonId:
|
||||
if debug:
|
||||
print('DEBUG: No actor was found for '+handle)
|
||||
return 4
|
||||
|
||||
# Get the json for the c2s post, not saving anything to file
|
||||
# Note that baseDir is set to None
|
||||
saveToFile=False
|
||||
clientToServer=True
|
||||
toDomainFull=toDomain
|
||||
if toPort!=80 and toDomain!=443:
|
||||
toDomainFull=toDomain+':'+str(toPort)
|
||||
toPersonId=httpPrefix+'://'+toDomainFull+'/users/'+toNickname
|
||||
postJsonObject = \
|
||||
createPostBase(None, \
|
||||
fromNickname,fromDomain,fromPort, \
|
||||
toPersonId,cc,httpPrefix,content, \
|
||||
followersOnly,saveToFile,clientToServer, \
|
||||
attachImageFilename,imageDescription,useBlurhash, \
|
||||
inReplyTo,inReplyToAtomUri,subject)
|
||||
|
||||
authHeader=createBasicAuthHeader(fromNickname,password)
|
||||
headers = {'host': fromDomain, \
|
||||
'Content-type': 'application/json', \
|
||||
'Authorization': authHeader}
|
||||
postResult = \
|
||||
postJson(session,postJsonObject,[],inboxUrl,headers,"inbox:write")
|
||||
if not postResult:
|
||||
if debug:
|
||||
print('DEBUG: POST failed for c2s to '+inboxUrl)
|
||||
return 5
|
||||
|
||||
if debug:
|
||||
print('DEBUG: c2s POST success')
|
||||
return 0
|
||||
|
||||
def groupFollowersByDomain(baseDir :str,nickname :str,domain :str) -> {}:
|
||||
"""Returns a dictionary with followers grouped by domain
|
||||
"""
|
||||
|
@ -686,6 +762,9 @@ def sendSignedJson(postJsonObject: {},session,baseDir: str, \
|
|||
personCache: {}, debug: bool) -> int:
|
||||
"""Sends a signed json object to an inbox/outbox
|
||||
"""
|
||||
if not session:
|
||||
print('WARN: No session specified for sendSignedJson')
|
||||
return 8
|
||||
withDigest=True
|
||||
|
||||
sharedInbox=False
|
||||
|
@ -773,13 +852,13 @@ def sendToNamedAddresses(session,baseDir: str, \
|
|||
postJsonObject: {},debug: bool) -> None:
|
||||
"""sends a post to the specific named addresses in to/cc
|
||||
"""
|
||||
if port!=80 and port!=443:
|
||||
domain=domain+':'+str(port)
|
||||
|
||||
if not session:
|
||||
print('WARN: No session for sendToNamedAddresses')
|
||||
return
|
||||
if not postJsonObject.get('object'):
|
||||
return False
|
||||
return
|
||||
if not postJsonObject['object'].get('to'):
|
||||
return False
|
||||
return
|
||||
|
||||
recipients=[]
|
||||
recipientType=['to','cc']
|
||||
|
@ -793,7 +872,7 @@ def sendToNamedAddresses(session,baseDir: str, \
|
|||
if not recipients:
|
||||
return
|
||||
if debug:
|
||||
print('c2s sending to addresses: '+str(recipients))
|
||||
print('Sending individually addressed posts: '+str(recipients))
|
||||
# this is after the message has arrived at the server
|
||||
clientToServer=False
|
||||
for address in recipients:
|
||||
|
@ -804,7 +883,16 @@ def sendToNamedAddresses(session,baseDir: str, \
|
|||
if not toDomain:
|
||||
continue
|
||||
if debug:
|
||||
print('c2s sending from '+nickname+'@'+domain+' to '+toNickname+'@'+toDomain)
|
||||
domainFull=domain
|
||||
if port:
|
||||
if port!=80 and port!=443:
|
||||
domainFull=domain+':'+str(port)
|
||||
toDomainFull=toDomain
|
||||
if toPort:
|
||||
if toPort!=80 and toPort!=443:
|
||||
toDomainFull=toDomain+':'+str(toPort)
|
||||
print('Post sending s2s: '+nickname+'@'+domainFull+' to '+toNickname+'@'+toDomainFull)
|
||||
cc=[]
|
||||
sendSignedJson(postJsonObject,session,baseDir, \
|
||||
nickname,domain,port, \
|
||||
toNickname,toDomain,toPort, \
|
||||
|
@ -821,6 +909,9 @@ def sendToFollowers(session,baseDir: str, \
|
|||
postJsonObject: {},debug: bool) -> None:
|
||||
"""sends a post to the followers of the given nickname
|
||||
"""
|
||||
if not session:
|
||||
print('WARN: No session for sendToFollowers')
|
||||
return
|
||||
if not postIsAddressedToFollowers(baseDir,nickname,domain, \
|
||||
port,httpPrefix,postJsonObject):
|
||||
if debug:
|
||||
|
|
|
@ -31,6 +31,8 @@ def getJson(session,url: str,headers: {},params: {}) -> {}:
|
|||
if params:
|
||||
sessionParams=params
|
||||
sessionHeaders['User-agent'] = "Mozilla/5.0 (Linux; Android 5.1.1; Nexus 5 Build/LMY48B; wv)"
|
||||
if not session:
|
||||
print('WARN: no session specified for getJson')
|
||||
session.cookies.clear()
|
||||
try:
|
||||
result=session.get(url, headers=sessionHeaders, params=sessionParams)
|
||||
|
|
89
tests.py
89
tests.py
|
@ -29,6 +29,7 @@ from posts import noOfFollowersOnDomain
|
|||
from posts import groupFollowersByDomain
|
||||
from posts import sendCapabilitiesUpdate
|
||||
from posts import archivePostsForPerson
|
||||
from posts import sendPostViaServer
|
||||
from follow import clearFollows
|
||||
from follow import clearFollowers
|
||||
from utils import followPerson
|
||||
|
@ -136,7 +137,6 @@ def createServerAlice(path: str,domain: str,port: int,federationList: [], \
|
|||
nickname='alice'
|
||||
httpPrefix='http'
|
||||
useTor=False
|
||||
clientToServer=False
|
||||
password='alicepass'
|
||||
noreply=False
|
||||
nolike=False
|
||||
|
@ -954,7 +954,94 @@ def testAuthentication():
|
|||
|
||||
os.chdir(currDir)
|
||||
shutil.rmtree(baseDir)
|
||||
|
||||
def testClientToServer():
|
||||
print('Testing sending a post via c2s')
|
||||
|
||||
global testServerAliceRunning
|
||||
global testServerBobRunning
|
||||
testServerAliceRunning = False
|
||||
testServerBobRunning = False
|
||||
|
||||
httpPrefix='http'
|
||||
useTor=False
|
||||
federationList=[]
|
||||
|
||||
baseDir=os.getcwd()
|
||||
if os.path.isdir(baseDir+'/.tests'):
|
||||
shutil.rmtree(baseDir+'/.tests')
|
||||
os.mkdir(baseDir+'/.tests')
|
||||
|
||||
ocapAlways=False
|
||||
|
||||
# create the servers
|
||||
aliceDir=baseDir+'/.tests/alice'
|
||||
aliceDomain='127.0.0.42'
|
||||
alicePort=61935
|
||||
thrAlice = \
|
||||
threadWithTrace(target=createServerAlice, \
|
||||
args=(aliceDir,aliceDomain,alicePort, \
|
||||
federationList,False,False, \
|
||||
ocapAlways),daemon=True)
|
||||
|
||||
bobDir=baseDir+'/.tests/bob'
|
||||
bobDomain='127.0.0.64'
|
||||
bobPort=61936
|
||||
thrBob = \
|
||||
threadWithTrace(target=createServerBob, \
|
||||
args=(bobDir,bobDomain,bobPort, \
|
||||
federationList,False,False, \
|
||||
ocapAlways),daemon=True)
|
||||
|
||||
thrAlice.start()
|
||||
thrBob.start()
|
||||
assert thrAlice.isAlive()==True
|
||||
assert thrBob.isAlive()==True
|
||||
|
||||
# wait for both servers to be running
|
||||
ctr=0
|
||||
while not (testServerAliceRunning and testServerBobRunning):
|
||||
time.sleep(1)
|
||||
ctr+=1
|
||||
if ctr>60:
|
||||
break
|
||||
print('Alice online: '+str(testServerAliceRunning))
|
||||
print('Bob online: '+str(testServerBobRunning))
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
print('\n\n*******************************************************')
|
||||
print('Alice sends to Bob via c2s')
|
||||
|
||||
sessionAlice = createSession(aliceDomain,alicePort,useTor)
|
||||
followersOnly=False
|
||||
attachImageFilename=None
|
||||
imageDescription=None
|
||||
useBlurhash=False
|
||||
cachedWebfingers={}
|
||||
personCache={}
|
||||
password='alicepass'
|
||||
outboxPath=aliceDir+'/accounts/alice@'+aliceDomain+'/outbox'
|
||||
assert len([name for name in os.listdir(outboxPath) if os.path.isfile(os.path.join(outboxPath, name))])==0
|
||||
sendResult= \
|
||||
sendPostViaServer(sessionAlice,'alice',password, \
|
||||
aliceDomain,alicePort, \
|
||||
'bob',bobDomain,bobPort,None, \
|
||||
httpPrefix,'Sent from my ActivityPub client',followersOnly, \
|
||||
attachImageFilename,imageDescription,useBlurhash, \
|
||||
cachedWebfingers,personCache, \
|
||||
True,None,None,None)
|
||||
print('sendResult: '+str(sendResult))
|
||||
assert sendResult==0
|
||||
|
||||
for i in range(30):
|
||||
if os.path.isdir(outboxPath):
|
||||
if len([name for name in os.listdir(outboxPath) if os.path.isfile(os.path.join(outboxPath, name))])==1:
|
||||
break
|
||||
time.sleep(1)
|
||||
|
||||
assert len([name for name in os.listdir(outboxPath) if os.path.isfile(os.path.join(outboxPath, name))])==1
|
||||
|
||||
def runAllTests():
|
||||
print('Running tests...')
|
||||
testHttpsig()
|
||||
|
|
|
@ -37,6 +37,10 @@ def parseHandle(handle: str) -> (str,str):
|
|||
|
||||
|
||||
def webfingerHandle(session,handle: str,httpPrefix: str,cachedWebfingers: {}) -> {}:
|
||||
if not session:
|
||||
print('WARN: No session specified for webfingerHandle')
|
||||
return None
|
||||
|
||||
nickname, domain = parseHandle(handle)
|
||||
if not nickname:
|
||||
return None
|
||||
|
@ -49,6 +53,9 @@ def webfingerHandle(session,handle: str,httpPrefix: str,cachedWebfingers: {}) ->
|
|||
url = '{}://{}/.well-known/webfinger'.format(httpPrefix,domain)
|
||||
par = {'resource': 'acct:{}'.format(nickname+'@'+wfDomain)}
|
||||
hdr = {'Accept': 'application/jrd+json'}
|
||||
#print('webfinger url: '+url)
|
||||
#print('webfinger par: '+str(par))
|
||||
#print('webfinger hdr: '+str(hdr))
|
||||
try:
|
||||
result = getJson(session, url, hdr, par)
|
||||
except:
|
||||
|
|
Loading…
Reference in New Issue