forked from indymedia/epicyon
Change username to nickname
parent
983b299792
commit
c2cc03c76a
|
@ -12,7 +12,7 @@ from utils import getStatusNumber
|
||||||
from utils import createOutboxDir
|
from utils import createOutboxDir
|
||||||
from utils import urlPermitted
|
from utils import urlPermitted
|
||||||
|
|
||||||
def createAcceptReject(baseDir: str,federationList: [],username: str,domain: str,port: int,toUrl: str,ccUrl: str,https: bool,objectUrl: str,acceptType: str) -> {}:
|
def createAcceptReject(baseDir: str,federationList: [],nickname: str,domain: str,port: int,toUrl: str,ccUrl: str,https: bool,objectUrl: str,acceptType: str) -> {}:
|
||||||
"""Accepts or rejects something (eg. a follow request)
|
"""Accepts or rejects something (eg. a follow request)
|
||||||
Typically toUrl will be https://www.w3.org/ns/activitystreams#Public
|
Typically toUrl will be https://www.w3.org/ns/activitystreams#Public
|
||||||
and ccUrl might be a specific person favorited or repeated and the followers url
|
and ccUrl might be a specific person favorited or repeated and the followers url
|
||||||
|
@ -30,7 +30,7 @@ def createAcceptReject(baseDir: str,federationList: [],username: str,domain: str
|
||||||
|
|
||||||
newAccept = {
|
newAccept = {
|
||||||
'type': acceptType,
|
'type': acceptType,
|
||||||
'actor': prefix+'://'+domain+'/users/'+username,
|
'actor': prefix+'://'+domain+'/users/'+nickname,
|
||||||
'to': [toUrl],
|
'to': [toUrl],
|
||||||
'cc': [],
|
'cc': [],
|
||||||
'object': objectUrl
|
'object': objectUrl
|
||||||
|
@ -40,8 +40,8 @@ def createAcceptReject(baseDir: str,federationList: [],username: str,domain: str
|
||||||
newAccept['cc']=ccUrl
|
newAccept['cc']=ccUrl
|
||||||
return newAccept
|
return newAccept
|
||||||
|
|
||||||
def createAccept(baseDir: str,federationList: [],username: str,domain: str,port: int,toUrl: str,ccUrl: str,https: bool,objectUrl: str) -> {}:
|
def createAccept(baseDir: str,federationList: [],nickname: str,domain: str,port: int,toUrl: str,ccUrl: str,https: bool,objectUrl: str) -> {}:
|
||||||
return createAcceptReject(baseDir,federationList,username,domain,port,toUrl,ccUrl,https,objectUrl,'Accept')
|
return createAcceptReject(baseDir,federationList,nickname,domain,port,toUrl,ccUrl,https,objectUrl,'Accept')
|
||||||
|
|
||||||
def createReject(baseDir: str,federationList: [],username: str,domain: str,port: int,toUrl: str,ccUrl: str,https: bool,objectUrl: str) -> {}:
|
def createReject(baseDir: str,federationList: [],nickname: str,domain: str,port: int,toUrl: str,ccUrl: str,https: bool,objectUrl: str) -> {}:
|
||||||
return createAcceptReject(baseDir,federationList,username,domain,port,toUrl,ccUrl,https,objectUrl,'Reject')
|
return createAcceptReject(baseDir,federationList,nickname,domain,port,toUrl,ccUrl,https,objectUrl,'Reject')
|
||||||
|
|
24
announce.py
24
announce.py
|
@ -13,7 +13,7 @@ from utils import createOutboxDir
|
||||||
from utils import urlPermitted
|
from utils import urlPermitted
|
||||||
|
|
||||||
def createAnnounce(baseDir: str,federationList: [], \
|
def createAnnounce(baseDir: str,federationList: [], \
|
||||||
username: str, domain: str, port: int, \
|
nickname: str, domain: str, port: int, \
|
||||||
toUrl: str, ccUrl: str, https: bool, \
|
toUrl: str, ccUrl: str, https: bool, \
|
||||||
objectUrl: str, saveToFile: bool) -> {}:
|
objectUrl: str, saveToFile: bool) -> {}:
|
||||||
"""Creates an announce message
|
"""Creates an announce message
|
||||||
|
@ -32,10 +32,10 @@ def createAnnounce(baseDir: str,federationList: [], \
|
||||||
domain=domain+':'+str(port)
|
domain=domain+':'+str(port)
|
||||||
|
|
||||||
statusNumber,published = getStatusNumber()
|
statusNumber,published = getStatusNumber()
|
||||||
newAnnounceId=prefix+'://'+domain+'/users/'+username+'/statuses/'+statusNumber
|
newAnnounceId=prefix+'://'+domain+'/users/'+nickname+'/statuses/'+statusNumber
|
||||||
newAnnounce = {
|
newAnnounce = {
|
||||||
'actor': prefix+'://'+domain+'/users/'+username,
|
'actor': prefix+'://'+domain+'/users/'+nickname,
|
||||||
'atomUri': prefix+'://'+domain+'/users/'+username+'/statuses/'+statusNumber,
|
'atomUri': prefix+'://'+domain+'/users/'+nickname+'/statuses/'+statusNumber,
|
||||||
'cc': [],
|
'cc': [],
|
||||||
'id': newAnnounceId+'/activity',
|
'id': newAnnounceId+'/activity',
|
||||||
'object': objectUrl,
|
'object': objectUrl,
|
||||||
|
@ -49,14 +49,14 @@ def createAnnounce(baseDir: str,federationList: [], \
|
||||||
if saveToFile:
|
if saveToFile:
|
||||||
if ':' in domain:
|
if ':' in domain:
|
||||||
domain=domain.split(':')[0]
|
domain=domain.split(':')[0]
|
||||||
outboxDir = createOutboxDir(username,domain,baseDir)
|
outboxDir = createOutboxDir(nickname,domain,baseDir)
|
||||||
filename=outboxDir+'/'+newAnnounceId.replace('/','#')+'.json'
|
filename=outboxDir+'/'+newAnnounceId.replace('/','#')+'.json'
|
||||||
with open(filename, 'w') as fp:
|
with open(filename, 'w') as fp:
|
||||||
commentjson.dump(newAnnounce, fp, indent=4, sort_keys=False)
|
commentjson.dump(newAnnounce, fp, indent=4, sort_keys=False)
|
||||||
return newAnnounce
|
return newAnnounce
|
||||||
|
|
||||||
def announcePublic(baseDir: str,federationList: [], \
|
def announcePublic(baseDir: str,federationList: [], \
|
||||||
username: str, domain: str, port: int, https: bool, \
|
nickname: str, domain: str, port: int, https: bool, \
|
||||||
objectUrl: str, saveToFile: bool) -> {}:
|
objectUrl: str, saveToFile: bool) -> {}:
|
||||||
"""Makes a public announcement
|
"""Makes a public announcement
|
||||||
"""
|
"""
|
||||||
|
@ -69,13 +69,13 @@ def announcePublic(baseDir: str,federationList: [], \
|
||||||
fromDomain=fromDomain+':'+str(port)
|
fromDomain=fromDomain+':'+str(port)
|
||||||
|
|
||||||
toUrl = 'https://www.w3.org/ns/activitystreams#Public'
|
toUrl = 'https://www.w3.org/ns/activitystreams#Public'
|
||||||
ccUrl = prefix + '://'+fromDomain+'/users/'+username+'/followers'
|
ccUrl = prefix + '://'+fromDomain+'/users/'+nickname+'/followers'
|
||||||
return createAnnounce(baseDir,username, domain, port, \
|
return createAnnounce(baseDir,nickname, domain, port, \
|
||||||
toUrl, ccUrl, https, objectUrl, saveToFile)
|
toUrl, ccUrl, https, objectUrl, saveToFile)
|
||||||
|
|
||||||
def repeatPost(baseDir: str,federationList: [], \
|
def repeatPost(baseDir: str,federationList: [], \
|
||||||
username: str, domain: str, port: int, https: bool, \
|
nickname: str, domain: str, port: int, https: bool, \
|
||||||
announceUsername: str, announceDomain: str, \
|
announceNickname: str, announceDomain: str, \
|
||||||
announcePort: int, announceHttps: bool, \
|
announcePort: int, announceHttps: bool, \
|
||||||
announceStatusNumber: int, saveToFile: bool) -> {}:
|
announceStatusNumber: int, saveToFile: bool) -> {}:
|
||||||
"""Repeats a given status post
|
"""Repeats a given status post
|
||||||
|
@ -89,7 +89,7 @@ def repeatPost(baseDir: str,federationList: [], \
|
||||||
announcedDomain=announcedDomain+':'+str(announcePort)
|
announcedDomain=announcedDomain+':'+str(announcePort)
|
||||||
|
|
||||||
objectUrl = prefix + '://'+announcedDomain+'/users/'+ \
|
objectUrl = prefix + '://'+announcedDomain+'/users/'+ \
|
||||||
announceUsername+'/statuses/'+str(announceStatusNumber)
|
announceNickname+'/statuses/'+str(announceStatusNumber)
|
||||||
|
|
||||||
return announcePublic(baseDir,username, domain, port, https, objectUrl, saveToFile)
|
return announcePublic(baseDir,nickname, domain, port, https, objectUrl, saveToFile)
|
||||||
|
|
||||||
|
|
|
@ -43,9 +43,9 @@ def readFollowList(filename: str):
|
||||||
followUsers = open(filename, "r")
|
followUsers = open(filename, "r")
|
||||||
for u in followUsers:
|
for u in followUsers:
|
||||||
if u not in followlist:
|
if u not in followlist:
|
||||||
username,domain = parseHandle(u)
|
nickname,domain = parseHandle(u)
|
||||||
if username:
|
if nickname:
|
||||||
followlist.append(username+'@'+domain)
|
followlist.append(nickname+'@'+domain)
|
||||||
followUsers.close()
|
followUsers.close()
|
||||||
return followlist
|
return followlist
|
||||||
|
|
||||||
|
|
29
epicyon.py
29
epicyon.py
|
@ -7,7 +7,7 @@ __email__ = "bob@freedombone.net"
|
||||||
__status__ = "Production"
|
__status__ = "Production"
|
||||||
|
|
||||||
from person import createPerson
|
from person import createPerson
|
||||||
from person import setPreferredUsername
|
from person import setPreferredNickname
|
||||||
from person import setBio
|
from person import setBio
|
||||||
from webfinger import webfingerHandle
|
from webfinger import webfingerHandle
|
||||||
from posts import getPosts
|
from posts import getPosts
|
||||||
|
@ -73,7 +73,7 @@ if args.tests:
|
||||||
print(args.domain)
|
print(args.domain)
|
||||||
print(str(args.federationList))
|
print(str(args.federationList))
|
||||||
|
|
||||||
username='admin'
|
nickname='admin'
|
||||||
domain=args.domain
|
domain=args.domain
|
||||||
port=args.port
|
port=args.port
|
||||||
https=args.https
|
https=args.https
|
||||||
|
@ -93,24 +93,13 @@ session = createSession(domain,port,useTor)
|
||||||
personCache={}
|
personCache={}
|
||||||
cachedWebfingers={}
|
cachedWebfingers={}
|
||||||
|
|
||||||
#unfollowPerson(username,domain,'squirrel','secret.com')
|
privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(baseDir,nickname,domain,port,https,True)
|
||||||
#sys.exit()
|
#deleteAllPosts(nickname,domain)
|
||||||
|
setPreferredNickname(baseDir,nickname,domain,'badger')
|
||||||
#asHeader = {'Accept': 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'}
|
setBio(baseDir,nickname,domain,'Some personal info')
|
||||||
#userFollowing = getJson(session,"https://mastodon.social/users/Gargron/followers?page=true",asHeader,None)
|
#createPublicPost(baseDir,nickname, domain, port,https, "G'day world!", False, True, None, None, 'Not suitable for Vogons')
|
||||||
#userFollowing = getJson(session,"https://mastodon.social/users/Gargron/following",asHeader,None)
|
#archivePosts(nickname,domain,baseDir,4)
|
||||||
#userFollowing = getJson(session,"https://mastodon.social/users/Gargron/following?page=1",asHeader,None)
|
#outboxJson=createOutbox(baseDir,nickname,domain,port,https,2,True,None)
|
||||||
#pprint(userFollowing)
|
|
||||||
#sys.exit()
|
|
||||||
|
|
||||||
|
|
||||||
privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(baseDir,username,domain,port,https,True)
|
|
||||||
#deleteAllPosts(username,domain)
|
|
||||||
setPreferredUsername(baseDir,username,domain,'badger')
|
|
||||||
setBio(baseDir,username,domain,'Some personal info')
|
|
||||||
#createPublicPost(baseDir,username, domain, port,https, "G'day world!", False, True, None, None, 'Not suitable for Vogons')
|
|
||||||
#archivePosts(username,domain,baseDir,4)
|
|
||||||
#outboxJson=createOutbox(baseDir,username,domain,port,https,2,True,None)
|
|
||||||
#pprint(outboxJson)
|
#pprint(outboxJson)
|
||||||
|
|
||||||
#testPostMessageBetweenServers()
|
#testPostMessageBetweenServers()
|
||||||
|
|
90
follow.py
90
follow.py
|
@ -10,18 +10,18 @@ import json
|
||||||
from pprint import pprint
|
from pprint import pprint
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from person import validUsername
|
from person import validNickname
|
||||||
from utils import domainPermitted
|
from utils import domainPermitted
|
||||||
|
|
||||||
def followPerson(baseDir: str,username: str, domain: str, \
|
def followPerson(baseDir: str,nickname: str, domain: str, \
|
||||||
followUsername: str, followDomain: str, \
|
followNickname: str, followDomain: str, \
|
||||||
federationList: [], followFile='following.txt') -> bool:
|
federationList: [], followFile='following.txt') -> bool:
|
||||||
"""Adds a person to the follow list
|
"""Adds a person to the follow list
|
||||||
"""
|
"""
|
||||||
if not domainPermitted(followDomain.lower().replace('\n',''), federationList):
|
if not domainPermitted(followDomain.lower().replace('\n',''), federationList):
|
||||||
return False
|
return False
|
||||||
handle=username.lower()+'@'+domain.lower()
|
handle=nickname.lower()+'@'+domain.lower()
|
||||||
handleToFollow=followUsername.lower()+'@'+followDomain.lower()
|
handleToFollow=followNickname.lower()+'@'+followDomain.lower()
|
||||||
if not os.path.isdir(baseDir+'/accounts'):
|
if not os.path.isdir(baseDir+'/accounts'):
|
||||||
os.mkdir(baseDir+'/accounts')
|
os.mkdir(baseDir+'/accounts')
|
||||||
if not os.path.isdir(baseDir+'/accounts/'+handle):
|
if not os.path.isdir(baseDir+'/accounts/'+handle):
|
||||||
|
@ -37,22 +37,22 @@ def followPerson(baseDir: str,username: str, domain: str, \
|
||||||
followfile.write(handleToFollow+'\n')
|
followfile.write(handleToFollow+'\n')
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def followerOfPerson(baseDir: str,username: str, domain: str, \
|
def followerOfPerson(baseDir: str,nickname: str, domain: str, \
|
||||||
followerUsername: str, followerDomain: str, \
|
followerNickname: str, followerDomain: str, \
|
||||||
federationList: []) -> bool:
|
federationList: []) -> bool:
|
||||||
"""Adds a follower of the given person
|
"""Adds a follower of the given person
|
||||||
"""
|
"""
|
||||||
return followPerson(baseDir,username, domain, \
|
return followPerson(baseDir,nickname, domain, \
|
||||||
followerUsername, followerDomain, \
|
followerNickname, followerDomain, \
|
||||||
federationList, 'followers.txt')
|
federationList, 'followers.txt')
|
||||||
|
|
||||||
def unfollowPerson(baseDir: str,username: str, domain: str, \
|
def unfollowPerson(baseDir: str,nickname: str, domain: str, \
|
||||||
followUsername: str, followDomain: str, \
|
followNickname: str, followDomain: str, \
|
||||||
followFile='following.txt') -> None:
|
followFile='following.txt') -> None:
|
||||||
"""Removes a person to the follow list
|
"""Removes a person to the follow list
|
||||||
"""
|
"""
|
||||||
handle=username.lower()+'@'+domain.lower()
|
handle=nickname.lower()+'@'+domain.lower()
|
||||||
handleToUnfollow=followUsername.lower()+'@'+followDomain.lower()
|
handleToUnfollow=followNickname.lower()+'@'+followDomain.lower()
|
||||||
if not os.path.isdir(baseDir+'/accounts'):
|
if not os.path.isdir(baseDir+'/accounts'):
|
||||||
os.mkdir(baseDir+'/accounts')
|
os.mkdir(baseDir+'/accounts')
|
||||||
if not os.path.isdir(baseDir+'/accounts/'+handle):
|
if not os.path.isdir(baseDir+'/accounts/'+handle):
|
||||||
|
@ -68,16 +68,16 @@ def unfollowPerson(baseDir: str,username: str, domain: str, \
|
||||||
if line.strip("\n") != handleToUnfollow:
|
if line.strip("\n") != handleToUnfollow:
|
||||||
f.write(line)
|
f.write(line)
|
||||||
|
|
||||||
def unfollowerOfPerson(baseDir: str,username: str,domain: str, \
|
def unfollowerOfPerson(baseDir: str,nickname: str,domain: str, \
|
||||||
followerUsername: str,followerDomain: str) -> None:
|
followerNickname: str,followerDomain: str) -> None:
|
||||||
"""Remove a follower of a person
|
"""Remove a follower of a person
|
||||||
"""
|
"""
|
||||||
unfollowPerson(baseDir,username,domain,followerUsername,followerDomain,'followers.txt')
|
unfollowPerson(baseDir,nickname,domain,followerNickname,followerDomain,'followers.txt')
|
||||||
|
|
||||||
def clearFollows(baseDir: str,username: str,domain: str,followFile='following.txt') -> None:
|
def clearFollows(baseDir: str,nickname: str,domain: str,followFile='following.txt') -> None:
|
||||||
"""Removes all follows
|
"""Removes all follows
|
||||||
"""
|
"""
|
||||||
handle=username.lower()+'@'+domain.lower()
|
handle=nickname.lower()+'@'+domain.lower()
|
||||||
if not os.path.isdir(baseDir+'/accounts'):
|
if not os.path.isdir(baseDir+'/accounts'):
|
||||||
os.mkdir(baseDir+'/accounts')
|
os.mkdir(baseDir+'/accounts')
|
||||||
if not os.path.isdir(baseDir+'/accounts/'+handle):
|
if not os.path.isdir(baseDir+'/accounts/'+handle):
|
||||||
|
@ -86,15 +86,15 @@ def clearFollows(baseDir: str,username: str,domain: str,followFile='following.tx
|
||||||
if os.path.isfile(filename):
|
if os.path.isfile(filename):
|
||||||
os.remove(filename)
|
os.remove(filename)
|
||||||
|
|
||||||
def clearFollowers(baseDir: str,username: str,domain: str) -> None:
|
def clearFollowers(baseDir: str,nickname: str,domain: str) -> None:
|
||||||
"""Removes all followers
|
"""Removes all followers
|
||||||
"""
|
"""
|
||||||
clearFollows(baseDir,username, domain,'followers.txt')
|
clearFollows(baseDir,nickname, domain,'followers.txt')
|
||||||
|
|
||||||
def getNoOfFollows(baseDir: str,username: str,domain: str,followFile='following.txt') -> int:
|
def getNoOfFollows(baseDir: str,nickname: str,domain: str,followFile='following.txt') -> int:
|
||||||
"""Returns the number of follows or followers
|
"""Returns the number of follows or followers
|
||||||
"""
|
"""
|
||||||
handle=username.lower()+'@'+domain.lower()
|
handle=nickname.lower()+'@'+domain.lower()
|
||||||
filename=baseDir+'/accounts/'+handle+'/'+followFile
|
filename=baseDir+'/accounts/'+handle+'/'+followFile
|
||||||
if not os.path.isfile(filename):
|
if not os.path.isfile(filename):
|
||||||
return 0
|
return 0
|
||||||
|
@ -109,10 +109,10 @@ def getNoOfFollows(baseDir: str,username: str,domain: str,followFile='following.
|
||||||
ctr += 1
|
ctr += 1
|
||||||
return ctr
|
return ctr
|
||||||
|
|
||||||
def getNoOfFollowers(baseDir: str,username: str,domain: str) -> int:
|
def getNoOfFollowers(baseDir: str,nickname: str,domain: str) -> int:
|
||||||
"""Returns the number of followers of the given person
|
"""Returns the number of followers of the given person
|
||||||
"""
|
"""
|
||||||
return getNoOfFollows(baseDir,username,domain,'followers.txt')
|
return getNoOfFollows(baseDir,nickname,domain,'followers.txt')
|
||||||
|
|
||||||
def getFollowingFeed(baseDir: str,domain: str,port: int,path: str,https: bool, \
|
def getFollowingFeed(baseDir: str,domain: str,port: int,path: str,https: bool, \
|
||||||
followsPerPage=12,followFile='following') -> {}:
|
followsPerPage=12,followFile='following') -> {}:
|
||||||
|
@ -137,14 +137,14 @@ def getFollowingFeed(baseDir: str,domain: str,port: int,path: str,https: bool, \
|
||||||
|
|
||||||
if not path.endswith('/'+followFile):
|
if not path.endswith('/'+followFile):
|
||||||
return None
|
return None
|
||||||
username=None
|
nickname=None
|
||||||
if path.startswith('/users/'):
|
if path.startswith('/users/'):
|
||||||
username=path.replace('/users/','',1).replace('/'+followFile,'')
|
nickname=path.replace('/users/','',1).replace('/'+followFile,'')
|
||||||
if path.startswith('/@'):
|
if path.startswith('/@'):
|
||||||
username=path.replace('/@','',1).replace('/'+followFile,'')
|
nickname=path.replace('/@','',1).replace('/'+followFile,'')
|
||||||
if not username:
|
if not nickname:
|
||||||
return None
|
return None
|
||||||
if not validUsername(username):
|
if not validNickname(nickname):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
prefix='https'
|
prefix='https'
|
||||||
|
@ -157,9 +157,9 @@ def getFollowingFeed(baseDir: str,domain: str,port: int,path: str,https: bool, \
|
||||||
if headerOnly:
|
if headerOnly:
|
||||||
following = {
|
following = {
|
||||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||||
'first': prefix+'://'+domain+'/users/'+username+'/'+followFile+'?page=1',
|
'first': prefix+'://'+domain+'/users/'+nickname+'/'+followFile+'?page=1',
|
||||||
'id': prefix+'://'+domain+'/users/'+username+'/'+followFile,
|
'id': prefix+'://'+domain+'/users/'+nickname+'/'+followFile,
|
||||||
'totalItems': getNoOfFollows(username,domain),
|
'totalItems': getNoOfFollows(nickname,domain),
|
||||||
'type': 'OrderedCollection'}
|
'type': 'OrderedCollection'}
|
||||||
return following
|
return following
|
||||||
|
|
||||||
|
@ -169,13 +169,13 @@ def getFollowingFeed(baseDir: str,domain: str,port: int,path: str,https: bool, \
|
||||||
nextPageNumber=int(pageNumber+1)
|
nextPageNumber=int(pageNumber+1)
|
||||||
following = {
|
following = {
|
||||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||||
'id': prefix+'://'+domain+'/users/'+username+'/'+followFile+'?page='+str(pageNumber),
|
'id': prefix+'://'+domain+'/users/'+nickname+'/'+followFile+'?page='+str(pageNumber),
|
||||||
'orderedItems': [],
|
'orderedItems': [],
|
||||||
'partOf': prefix+'://'+domain+'/users/'+username+'/'+followFile,
|
'partOf': prefix+'://'+domain+'/users/'+nickname+'/'+followFile,
|
||||||
'totalItems': 0,
|
'totalItems': 0,
|
||||||
'type': 'OrderedCollectionPage'}
|
'type': 'OrderedCollectionPage'}
|
||||||
|
|
||||||
handle=username.lower()+'@'+domain.lower()
|
handle=nickname.lower()+'@'+domain.lower()
|
||||||
filename=baseDir+'/accounts/'+handle+'/'+followFile+'.txt'
|
filename=baseDir+'/accounts/'+handle+'/'+followFile+'.txt'
|
||||||
if not os.path.isfile(filename):
|
if not os.path.isfile(filename):
|
||||||
return following
|
return following
|
||||||
|
@ -206,7 +206,7 @@ def getFollowingFeed(baseDir: str,domain: str,port: int,path: str,https: bool, \
|
||||||
if lastPage<1:
|
if lastPage<1:
|
||||||
lastPage=1
|
lastPage=1
|
||||||
if nextPageNumber>lastPage:
|
if nextPageNumber>lastPage:
|
||||||
following['next']=prefix+'://'+domain+'/users/'+username+'/'+followFile+'?page='+str(lastPage)
|
following['next']=prefix+'://'+domain+'/users/'+nickname+'/'+followFile+'?page='+str(lastPage)
|
||||||
return following
|
return following
|
||||||
|
|
||||||
def receiveFollowRequest(baseDir: str,messageJson: {},federationList: []) -> bool:
|
def receiveFollowRequest(baseDir: str,messageJson: {},federationList: []) -> bool:
|
||||||
|
@ -219,8 +219,8 @@ def receiveFollowRequest(baseDir: str,messageJson: {},federationList: []) -> boo
|
||||||
domain=messageJson['actor'].split('/users/')[0].replace('https://','').replace('http://','')
|
domain=messageJson['actor'].split('/users/')[0].replace('https://','').replace('http://','')
|
||||||
if not domainPermitted(domain,federationList):
|
if not domainPermitted(domain,federationList):
|
||||||
return False
|
return False
|
||||||
username=messageJson['actor'].split('/users/')[1].replace('@','')
|
nickname=messageJson['actor'].split('/users/')[1].replace('@','')
|
||||||
handle=username.lower()+'@'+domain.lower()
|
handle=nickname.lower()+'@'+domain.lower()
|
||||||
if not os.path.isdir(baseDir+'/accounts/'+handle):
|
if not os.path.isdir(baseDir+'/accounts/'+handle):
|
||||||
return False
|
return False
|
||||||
if '/users/' not in messageJson['object']:
|
if '/users/' not in messageJson['object']:
|
||||||
|
@ -228,15 +228,15 @@ def receiveFollowRequest(baseDir: str,messageJson: {},federationList: []) -> boo
|
||||||
domainToFollow=messageJson['object'].split('/users/')[0].replace('https://','').replace('http://','')
|
domainToFollow=messageJson['object'].split('/users/')[0].replace('https://','').replace('http://','')
|
||||||
if not domainPermitted(domainToFollow,federationList):
|
if not domainPermitted(domainToFollow,federationList):
|
||||||
return False
|
return False
|
||||||
usernameToFollow=messageJson['object'].split('/users/')[1].replace('@','')
|
nicknameToFollow=messageJson['object'].split('/users/')[1].replace('@','')
|
||||||
handleToFollow=usernameToFollow.lower()+'@'+domainToFollow.lower()
|
handleToFollow=nicknameToFollow.lower()+'@'+domainToFollow.lower()
|
||||||
if domainToFollow==domain:
|
if domainToFollow==domain:
|
||||||
if not os.path.isdir(baseDir+'/accounts/'+handleToFollow):
|
if not os.path.isdir(baseDir+'/accounts/'+handleToFollow):
|
||||||
return False
|
return False
|
||||||
return followerOfPerson(baseDir,username,domain,usernameToFollow,domainToFollow,federationList)
|
return followerOfPerson(baseDir,nickname,domain,nicknameToFollow,domainToFollow,federationList)
|
||||||
|
|
||||||
def sendFollowRequest(baseDir: str,username: str,domain: str,port: int,https: bool, \
|
def sendFollowRequest(baseDir: str,nickname: str,domain: str,port: int,https: bool, \
|
||||||
followUsername: str,followDomain: str,followPort: bool,followHttps: bool, \
|
followNickname: str,followDomain: str,followPort: bool,followHttps: bool, \
|
||||||
federationList: []) -> {}:
|
federationList: []) -> {}:
|
||||||
"""Gets the json object for sending a follow request
|
"""Gets the json object for sending a follow request
|
||||||
"""
|
"""
|
||||||
|
@ -259,8 +259,8 @@ def sendFollowRequest(baseDir: str,username: str,domain: str,port: int,https: bo
|
||||||
|
|
||||||
newFollow = {
|
newFollow = {
|
||||||
'type': 'Follow',
|
'type': 'Follow',
|
||||||
'actor': prefix+'://'+domain+'/users/'+username,
|
'actor': prefix+'://'+domain+'/users/'+nickname,
|
||||||
'object': followPrefix+'://'+followDomain+'/users/'+followUsername,
|
'object': followPrefix+'://'+followDomain+'/users/'+followNickname,
|
||||||
'to': [toUrl],
|
'to': [toUrl],
|
||||||
'cc': []
|
'cc': []
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ from requests.auth import AuthBase
|
||||||
import base64
|
import base64
|
||||||
import json
|
import json
|
||||||
|
|
||||||
def signPostHeaders(privateKeyPem: str, username: str, domain: str, \
|
def signPostHeaders(privateKeyPem: str, nickname: str, domain: str, \
|
||||||
port: int,path: str, \
|
port: int,path: str, \
|
||||||
https: bool, messageBodyJson: {}) -> str:
|
https: bool, messageBodyJson: {}) -> str:
|
||||||
"""Returns a raw signature string that can be plugged into a header and
|
"""Returns a raw signature string that can be plugged into a header and
|
||||||
|
@ -28,7 +28,7 @@ def signPostHeaders(privateKeyPem: str, username: str, domain: str, \
|
||||||
if port!=80 and port!=443:
|
if port!=80 and port!=443:
|
||||||
domain=domain+':'+str(port)
|
domain=domain+':'+str(port)
|
||||||
|
|
||||||
keyID = prefix+'://'+domain+'/users/'+username+'/main-key'
|
keyID = prefix+'://'+domain+'/users/'+nickname+'/main-key'
|
||||||
if not messageBodyJson:
|
if not messageBodyJson:
|
||||||
headers = {'host': domain}
|
headers = {'host': domain}
|
||||||
else:
|
else:
|
||||||
|
@ -62,7 +62,7 @@ def signPostHeaders(privateKeyPem: str, username: str, domain: str, \
|
||||||
[f'{k}="{v}"' for k, v in signatureDict.items()])
|
[f'{k}="{v}"' for k, v in signatureDict.items()])
|
||||||
return signatureHeader
|
return signatureHeader
|
||||||
|
|
||||||
def createSignedHeader(privateKeyPem: str,username: str,domain: str,port: int, \
|
def createSignedHeader(privateKeyPem: str,nickname: str,domain: str,port: int, \
|
||||||
path: str,https: bool,withDigest: bool, \
|
path: str,https: bool,withDigest: bool, \
|
||||||
messageBodyJson: {}) -> {}:
|
messageBodyJson: {}) -> {}:
|
||||||
headerDomain=domain
|
headerDomain=domain
|
||||||
|
@ -78,7 +78,7 @@ def createSignedHeader(privateKeyPem: str,username: str,domain: str,port: int, \
|
||||||
base64.b64encode(SHA256.new(messageBodyJsonStr.encode()).digest())
|
base64.b64encode(SHA256.new(messageBodyJsonStr.encode()).digest())
|
||||||
headers = {'host': headerDomain, 'digest': f'SHA-256={bodyDigest}'}
|
headers = {'host': headerDomain, 'digest': f'SHA-256={bodyDigest}'}
|
||||||
path='/inbox'
|
path='/inbox'
|
||||||
signatureHeader = signPostHeaders(privateKeyPem, username, domain, port, \
|
signatureHeader = signPostHeaders(privateKeyPem, nickname, domain, port, \
|
||||||
path, https, None)
|
path, https, None)
|
||||||
headers['signature'] = signatureHeader
|
headers['signature'] = signatureHeader
|
||||||
headers['Content-type'] = 'application/json'
|
headers['Content-type'] = 'application/json'
|
||||||
|
|
12
like.py
12
like.py
|
@ -10,7 +10,7 @@ import json
|
||||||
import commentjson
|
import commentjson
|
||||||
from utils import urlPermitted
|
from utils import urlPermitted
|
||||||
|
|
||||||
def like(baseDir: str,federationList: [],username: str,domain: str,port: int, \
|
def like(baseDir: str,federationList: [],nickname: str,domain: str,port: int, \
|
||||||
toUrl: str,ccUrl: str,https: bool,objectUrl: str,saveToFile: bool) -> {}:
|
toUrl: str,ccUrl: str,https: bool,objectUrl: str,saveToFile: bool) -> {}:
|
||||||
"""Creates a like
|
"""Creates a like
|
||||||
Typically toUrl will be a followers collection
|
Typically toUrl will be a followers collection
|
||||||
|
@ -29,7 +29,7 @@ def like(baseDir: str,federationList: [],username: str,domain: str,port: int, \
|
||||||
|
|
||||||
newLike = {
|
newLike = {
|
||||||
'type': 'Like',
|
'type': 'Like',
|
||||||
'actor': prefix+'://'+domain+'/users/'+username,
|
'actor': prefix+'://'+domain+'/users/'+nickname,
|
||||||
'object': objectUrl,
|
'object': objectUrl,
|
||||||
'to': [toUrl],
|
'to': [toUrl],
|
||||||
'cc': []
|
'cc': []
|
||||||
|
@ -44,8 +44,8 @@ def like(baseDir: str,federationList: [],username: str,domain: str,port: int, \
|
||||||
return newLike
|
return newLike
|
||||||
|
|
||||||
def likePost(baseDir: str,federationList: [], \
|
def likePost(baseDir: str,federationList: [], \
|
||||||
username: str, domain: str, port: int, https: bool, \n
|
nickname: str, domain: str, port: int, https: bool, \n
|
||||||
likeUsername: str, likeDomain: str, likePort: int, likeHttps: bool, \n
|
likeNickname: str, likeDomain: str, likePort: int, likeHttps: bool, \n
|
||||||
likeStatusNumber: int,saveToFile: bool) -> {}:
|
likeStatusNumber: int,saveToFile: bool) -> {}:
|
||||||
"""Likes a given status post
|
"""Likes a given status post
|
||||||
"""
|
"""
|
||||||
|
@ -57,6 +57,6 @@ def likePost(baseDir: str,federationList: [], \
|
||||||
if likePort!=80 and likePort!=443:
|
if likePort!=80 and likePort!=443:
|
||||||
likeDomain=likeDomain+':'+str(likePort)
|
likeDomain=likeDomain+':'+str(likePort)
|
||||||
|
|
||||||
objectUrl = prefix + '://'+likeDomain+'/users/'+likeUsername+'/statuses/'+str(likeStatusNumber)
|
objectUrl = prefix + '://'+likeDomain+'/users/'+likeNickname+'/statuses/'+str(likeStatusNumber)
|
||||||
|
|
||||||
return like(baseDir,federationList,username,domain,port,toUrl,ccUrl,https,objectUrl,saveToFile)
|
return like(baseDir,federationList,nickname,domain,port,toUrl,ccUrl,https,objectUrl,saveToFile)
|
||||||
|
|
82
person.py
82
person.py
|
@ -21,7 +21,7 @@ def generateRSAKey() -> (str,str):
|
||||||
publicKeyPem = key.publickey().exportKey("PEM").decode("utf-8")
|
publicKeyPem = key.publickey().exportKey("PEM").decode("utf-8")
|
||||||
return privateKeyPem,publicKeyPem
|
return privateKeyPem,publicKeyPem
|
||||||
|
|
||||||
def createPerson(baseDir: str,username: str,domain: str,port: int, \
|
def createPerson(baseDir: str,nickname: str,domain: str,port: int, \
|
||||||
https: bool, saveToFile: bool) -> (str,str,{},{}):
|
https: bool, saveToFile: bool) -> (str,str,{},{}):
|
||||||
"""Returns the private key, public key, actor and webfinger endpoint
|
"""Returns the private key, public key, actor and webfinger endpoint
|
||||||
"""
|
"""
|
||||||
|
@ -31,11 +31,11 @@ def createPerson(baseDir: str,username: str,domain: str,port: int, \
|
||||||
|
|
||||||
privateKeyPem,publicKeyPem=generateRSAKey()
|
privateKeyPem,publicKeyPem=generateRSAKey()
|
||||||
webfingerEndpoint= \
|
webfingerEndpoint= \
|
||||||
createWebfingerEndpoint(username,domain,port,https,publicKeyPem)
|
createWebfingerEndpoint(nickname,domain,port,https,publicKeyPem)
|
||||||
if saveToFile:
|
if saveToFile:
|
||||||
storeWebfingerEndpoint(username,domain,baseDir,webfingerEndpoint)
|
storeWebfingerEndpoint(nickname,domain,baseDir,webfingerEndpoint)
|
||||||
|
|
||||||
handle=username.lower()+'@'+domain.lower()
|
handle=nickname.lower()+'@'+domain.lower()
|
||||||
if port!=80 and port!=443:
|
if port!=80 and port!=443:
|
||||||
domain=domain+':'+str(port)
|
domain=domain+':'+str(port)
|
||||||
|
|
||||||
|
@ -55,28 +55,28 @@ def createPerson(baseDir: str,username: str,domain: str,port: int, \
|
||||||
'value': 'schema:value'}],
|
'value': 'schema:value'}],
|
||||||
'attachment': [],
|
'attachment': [],
|
||||||
'endpoints': {'sharedInbox': prefix+'://'+domain+'/inbox'},
|
'endpoints': {'sharedInbox': prefix+'://'+domain+'/inbox'},
|
||||||
'featured': prefix+'://'+domain+'/users/'+username+'/collections/featured',
|
'featured': prefix+'://'+domain+'/users/'+nickname+'/collections/featured',
|
||||||
'followers': prefix+'://'+domain+'/users/'+username+'/followers',
|
'followers': prefix+'://'+domain+'/users/'+nickname+'/followers',
|
||||||
'following': prefix+'://'+domain+'/users/'+username+'/following',
|
'following': prefix+'://'+domain+'/users/'+nickname+'/following',
|
||||||
'icon': {'mediaType': 'image/png',
|
'icon': {'mediaType': 'image/png',
|
||||||
'type': 'Image',
|
'type': 'Image',
|
||||||
'url': prefix+'://'+domain+'/users/'+username+'_icon.png'},
|
'url': prefix+'://'+domain+'/users/'+nickname+'_icon.png'},
|
||||||
'id': prefix+'://'+domain+'/users/'+username,
|
'id': prefix+'://'+domain+'/users/'+nickname,
|
||||||
'image': {'mediaType': 'image/png',
|
'image': {'mediaType': 'image/png',
|
||||||
'type': 'Image',
|
'type': 'Image',
|
||||||
'url': prefix+'://'+domain+'/users/'+username+'.png'},
|
'url': prefix+'://'+domain+'/users/'+nickname+'.png'},
|
||||||
'inbox': prefix+'://'+domain+'/users/'+username+'/inbox',
|
'inbox': prefix+'://'+domain+'/users/'+nickname+'/inbox',
|
||||||
'manuallyApprovesFollowers': False,
|
'manuallyApprovesFollowers': False,
|
||||||
'name': username,
|
'name': nickname,
|
||||||
'outbox': prefix+'://'+domain+'/users/'+username+'/outbox',
|
'outbox': prefix+'://'+domain+'/users/'+nickname+'/outbox',
|
||||||
'preferredUsername': ''+username,
|
'preferredNickname': ''+nickname,
|
||||||
'publicKey': {'id': prefix+'://'+domain+'/users/'+username+'/main-key',
|
'publicKey': {'id': prefix+'://'+domain+'/users/'+nickname+'/main-key',
|
||||||
'owner': prefix+'://'+domain+'/users/'+username,
|
'owner': prefix+'://'+domain+'/users/'+nickname,
|
||||||
'publicKeyPem': publicKeyPem,
|
'publicKeyPem': publicKeyPem,
|
||||||
'summary': '',
|
'summary': '',
|
||||||
'tag': [],
|
'tag': [],
|
||||||
'type': 'Person',
|
'type': 'Person',
|
||||||
'url': prefix+'://'+domain+'/@'+username}
|
'url': prefix+'://'+domain+'/@'+nickname}
|
||||||
}
|
}
|
||||||
|
|
||||||
if saveToFile:
|
if saveToFile:
|
||||||
|
@ -108,26 +108,26 @@ def createPerson(baseDir: str,username: str,domain: str,port: int, \
|
||||||
|
|
||||||
return privateKeyPem,publicKeyPem,newPerson,webfingerEndpoint
|
return privateKeyPem,publicKeyPem,newPerson,webfingerEndpoint
|
||||||
|
|
||||||
def validUsername(username: str) -> bool:
|
def validNickname(nickname: str) -> bool:
|
||||||
forbiddenChars=['.',' ','/','?',':',';','@']
|
forbiddenChars=['.',' ','/','?',':',';','@']
|
||||||
for c in forbiddenChars:
|
for c in forbiddenChars:
|
||||||
if c in username:
|
if c in nickname:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def personKeyLookup(domain: str,path: str,baseDir: str) -> str:
|
def personKeyLookup(domain: str,path: str,baseDir: str) -> str:
|
||||||
"""Lookup the public key of the person with a given username
|
"""Lookup the public key of the person with a given nickname
|
||||||
"""
|
"""
|
||||||
if not path.endswith('/main-key'):
|
if not path.endswith('/main-key'):
|
||||||
return None
|
return None
|
||||||
if not path.startswith('/users/'):
|
if not path.startswith('/users/'):
|
||||||
return None
|
return None
|
||||||
username=path.replace('/users/','',1).replace('/main-key','')
|
nickname=path.replace('/users/','',1).replace('/main-key','')
|
||||||
if not validUsername(username):
|
if not validNickname(nickname):
|
||||||
return None
|
return None
|
||||||
if ':' in domain:
|
if ':' in domain:
|
||||||
domain=domain.split(':')[0]
|
domain=domain.split(':')[0]
|
||||||
handle=username.lower()+'@'+domain.lower()
|
handle=nickname.lower()+'@'+domain.lower()
|
||||||
filename=baseDir+'/accounts/'+handle.lower()+'.json'
|
filename=baseDir+'/accounts/'+handle.lower()+'.json'
|
||||||
if not os.path.isfile(filename):
|
if not os.path.isfile(filename):
|
||||||
return None
|
return None
|
||||||
|
@ -140,7 +140,7 @@ def personKeyLookup(domain: str,path: str,baseDir: str) -> str:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def personLookup(domain: str,path: str,baseDir: str) -> {}:
|
def personLookup(domain: str,path: str,baseDir: str) -> {}:
|
||||||
"""Lookup the person for an given username
|
"""Lookup the person for an given nickname
|
||||||
"""
|
"""
|
||||||
notPersonLookup=['/inbox','/outbox','/outboxarchive', \
|
notPersonLookup=['/inbox','/outbox','/outboxarchive', \
|
||||||
'/followers','/following','/featured', \
|
'/followers','/following','/featured', \
|
||||||
|
@ -149,18 +149,18 @@ def personLookup(domain: str,path: str,baseDir: str) -> {}:
|
||||||
for ending in notPersonLookup:
|
for ending in notPersonLookup:
|
||||||
if path.endswith(ending):
|
if path.endswith(ending):
|
||||||
return None
|
return None
|
||||||
username=None
|
nickname=None
|
||||||
if path.startswith('/users/'):
|
if path.startswith('/users/'):
|
||||||
username=path.replace('/users/','',1)
|
nickname=path.replace('/users/','',1)
|
||||||
if path.startswith('/@'):
|
if path.startswith('/@'):
|
||||||
username=path.replace('/@','',1)
|
nickname=path.replace('/@','',1)
|
||||||
if not username:
|
if not nickname:
|
||||||
return None
|
return None
|
||||||
if not validUsername(username):
|
if not validNickname(nickname):
|
||||||
return None
|
return None
|
||||||
if ':' in domain:
|
if ':' in domain:
|
||||||
domain=domain.split(':')[0]
|
domain=domain.split(':')[0]
|
||||||
handle=username.lower()+'@'+domain.lower()
|
handle=nickname.lower()+'@'+domain.lower()
|
||||||
filename=baseDir+'/accounts/'+handle.lower()+'.json'
|
filename=baseDir+'/accounts/'+handle.lower()+'.json'
|
||||||
if not os.path.isfile(filename):
|
if not os.path.isfile(filename):
|
||||||
return None
|
return None
|
||||||
|
@ -195,23 +195,23 @@ def personOutboxJson(baseDir: str,domain: str,port: int,path: str, \
|
||||||
|
|
||||||
if not path.endswith('/outbox'):
|
if not path.endswith('/outbox'):
|
||||||
return None
|
return None
|
||||||
username=None
|
nickname=None
|
||||||
if path.startswith('/users/'):
|
if path.startswith('/users/'):
|
||||||
username=path.replace('/users/','',1).replace('/outbox','')
|
nickname=path.replace('/users/','',1).replace('/outbox','')
|
||||||
if path.startswith('/@'):
|
if path.startswith('/@'):
|
||||||
username=path.replace('/@','',1).replace('/outbox','')
|
nickname=path.replace('/@','',1).replace('/outbox','')
|
||||||
if not username:
|
if not nickname:
|
||||||
return None
|
return None
|
||||||
if not validUsername(username):
|
if not validNickname(nickname):
|
||||||
return None
|
return None
|
||||||
return createOutbox(baseDir,username,domain,port,https, \
|
return createOutbox(baseDir,nickname,domain,port,https, \
|
||||||
noOfItems,headerOnly,pageNumber)
|
noOfItems,headerOnly,pageNumber)
|
||||||
|
|
||||||
def setPreferredUsername(baseDir: str,username: str, domain: str, \
|
def setPreferredNickname(baseDir: str,nickname: str, domain: str, \
|
||||||
preferredName: str) -> bool:
|
preferredName: str) -> bool:
|
||||||
if len(preferredName)>32:
|
if len(preferredName)>32:
|
||||||
return False
|
return False
|
||||||
handle=username.lower()+'@'+domain.lower()
|
handle=nickname.lower()+'@'+domain.lower()
|
||||||
filename=baseDir+'/accounts/'+handle.lower()+'.json'
|
filename=baseDir+'/accounts/'+handle.lower()+'.json'
|
||||||
if not os.path.isfile(filename):
|
if not os.path.isfile(filename):
|
||||||
return False
|
return False
|
||||||
|
@ -220,15 +220,15 @@ def setPreferredUsername(baseDir: str,username: str, domain: str, \
|
||||||
personJson=commentjson.load(fp)
|
personJson=commentjson.load(fp)
|
||||||
if not personJson:
|
if not personJson:
|
||||||
return False
|
return False
|
||||||
personJson['preferredUsername']=preferredName
|
personJson['preferredNickname']=preferredName
|
||||||
with open(filename, 'w') as fp:
|
with open(filename, 'w') as fp:
|
||||||
commentjson.dump(personJson, fp, indent=4, sort_keys=False)
|
commentjson.dump(personJson, fp, indent=4, sort_keys=False)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def setBio(baseDir: str,username: str, domain: str, bio: str) -> bool:
|
def setBio(baseDir: str,nickname: str, domain: str, bio: str) -> bool:
|
||||||
if len(bio)>32:
|
if len(bio)>32:
|
||||||
return False
|
return False
|
||||||
handle=username.lower()+'@'+domain.lower()
|
handle=nickname.lower()+'@'+domain.lower()
|
||||||
filename=baseDir+'/accounts/'+handle.lower()+'.json'
|
filename=baseDir+'/accounts/'+handle.lower()+'.json'
|
||||||
if not os.path.isfile(filename):
|
if not os.path.isfile(filename):
|
||||||
return False
|
return False
|
||||||
|
|
76
posts.py
76
posts.py
|
@ -34,10 +34,10 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
def getPersonKey(username: str,domain: str,baseDir: str,keyType='public'):
|
def getPersonKey(nickname: str,domain: str,baseDir: str,keyType='public'):
|
||||||
"""Returns the public or private key of a person
|
"""Returns the public or private key of a person
|
||||||
"""
|
"""
|
||||||
handle=username+'@'+domain
|
handle=nickname+'@'+domain
|
||||||
keyFilename=baseDir+'/keys/'+keyType+'/'+handle.lower()+'.key'
|
keyFilename=baseDir+'/keys/'+keyType+'/'+handle.lower()+'.key'
|
||||||
if not os.path.isfile(keyFilename):
|
if not os.path.isfile(keyFilename):
|
||||||
return ''
|
return ''
|
||||||
|
@ -212,10 +212,10 @@ def getPosts(session,outboxUrl: str,maxPosts: int,maxMentions: int, \
|
||||||
break
|
break
|
||||||
return personPosts
|
return personPosts
|
||||||
|
|
||||||
def createOutboxArchive(username: str,domain: str,baseDir: str) -> str:
|
def createOutboxArchive(nickname: str,domain: str,baseDir: str) -> str:
|
||||||
"""Creates an archive directory for outbox posts
|
"""Creates an archive directory for outbox posts
|
||||||
"""
|
"""
|
||||||
handle=username.lower()+'@'+domain.lower()
|
handle=nickname.lower()+'@'+domain.lower()
|
||||||
if not os.path.isdir(baseDir+'/accounts/'+handle):
|
if not os.path.isdir(baseDir+'/accounts/'+handle):
|
||||||
os.mkdir(baseDir+'/accounts/'+handle)
|
os.mkdir(baseDir+'/accounts/'+handle)
|
||||||
outboxArchiveDir=baseDir+'/accounts/'+handle+'/outboxarchive'
|
outboxArchiveDir=baseDir+'/accounts/'+handle+'/outboxarchive'
|
||||||
|
@ -223,10 +223,10 @@ def createOutboxArchive(username: str,domain: str,baseDir: str) -> str:
|
||||||
os.mkdir(outboxArchiveDir)
|
os.mkdir(outboxArchiveDir)
|
||||||
return outboxArchiveDir
|
return outboxArchiveDir
|
||||||
|
|
||||||
def deleteAllPosts(username: str, domain: str,baseDir: str) -> None:
|
def deleteAllPosts(nickname: str, domain: str,baseDir: str) -> None:
|
||||||
"""Deletes all posts for a person
|
"""Deletes all posts for a person
|
||||||
"""
|
"""
|
||||||
outboxDir = createOutboxDir(username,domain,baseDir)
|
outboxDir = createOutboxDir(nickname,domain,baseDir)
|
||||||
for deleteFilename in os.listdir(outboxDir):
|
for deleteFilename in os.listdir(outboxDir):
|
||||||
filePath = os.path.join(outboxDir, deleteFilename)
|
filePath = os.path.join(outboxDir, deleteFilename)
|
||||||
try:
|
try:
|
||||||
|
@ -236,7 +236,7 @@ def deleteAllPosts(username: str, domain: str,baseDir: str) -> None:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
print(e)
|
||||||
|
|
||||||
def createPostBase(baseDir: str,username: str, domain: str, port: int, \
|
def createPostBase(baseDir: str,nickname: str, domain: str, port: int, \
|
||||||
toUrl: str, ccUrl: str, https: bool, content: str, \
|
toUrl: str, ccUrl: str, https: bool, content: str, \
|
||||||
followersOnly: bool, saveToFile: bool, \
|
followersOnly: bool, saveToFile: bool, \
|
||||||
inReplyTo=None, inReplyToAtomUri=None, subject=None) -> {}:
|
inReplyTo=None, inReplyToAtomUri=None, subject=None) -> {}:
|
||||||
|
@ -253,11 +253,11 @@ def createPostBase(baseDir: str,username: str, domain: str, port: int, \
|
||||||
conversationDate=published.split('T')[0]
|
conversationDate=published.split('T')[0]
|
||||||
conversationId=statusNumber
|
conversationId=statusNumber
|
||||||
postTo='https://www.w3.org/ns/activitystreams#Public'
|
postTo='https://www.w3.org/ns/activitystreams#Public'
|
||||||
postCC=prefix+'://'+domain+'/users/'+username+'/followers'
|
postCC=prefix+'://'+domain+'/users/'+nickname+'/followers'
|
||||||
if followersOnly:
|
if followersOnly:
|
||||||
postTo=postCC
|
postTo=postCC
|
||||||
postCC=''
|
postCC=''
|
||||||
newPostId=prefix+'://'+domain+'/users/'+username+'/statuses/'+statusNumber
|
newPostId=prefix+'://'+domain+'/users/'+nickname+'/statuses/'+statusNumber
|
||||||
sensitive=False
|
sensitive=False
|
||||||
summary=None
|
summary=None
|
||||||
if subject:
|
if subject:
|
||||||
|
@ -266,7 +266,7 @@ def createPostBase(baseDir: str,username: str, domain: str, port: int, \
|
||||||
newPost = {
|
newPost = {
|
||||||
'id': newPostId+'/activity',
|
'id': newPostId+'/activity',
|
||||||
'type': 'Create',
|
'type': 'Create',
|
||||||
'actor': prefix+'://'+domain+'/users/'+username,
|
'actor': prefix+'://'+domain+'/users/'+nickname,
|
||||||
'published': published,
|
'published': published,
|
||||||
'to': [toUrl],
|
'to': [toUrl],
|
||||||
'cc': [],
|
'cc': [],
|
||||||
|
@ -275,12 +275,12 @@ def createPostBase(baseDir: str,username: str, domain: str, port: int, \
|
||||||
'summary': summary,
|
'summary': summary,
|
||||||
'inReplyTo': inReplyTo,
|
'inReplyTo': inReplyTo,
|
||||||
'published': published,
|
'published': published,
|
||||||
'url': prefix+'://'+domain+'/@'+username+'/'+statusNumber,
|
'url': prefix+'://'+domain+'/@'+nickname+'/'+statusNumber,
|
||||||
'attributedTo': prefix+'://'+domain+'/users/'+username,
|
'attributedTo': prefix+'://'+domain+'/users/'+nickname,
|
||||||
'to': [toUrl],
|
'to': [toUrl],
|
||||||
'cc': [],
|
'cc': [],
|
||||||
'sensitive': sensitive,
|
'sensitive': sensitive,
|
||||||
'atomUri': prefix+'://'+domain+'/users/'+username+'/statuses/'+statusNumber,
|
'atomUri': prefix+'://'+domain+'/users/'+nickname+'/statuses/'+statusNumber,
|
||||||
'inReplyToAtomUri': inReplyToAtomUri,
|
'inReplyToAtomUri': inReplyToAtomUri,
|
||||||
'conversation': 'tag:'+domain+','+conversationDate+':objectId='+conversationId+':objectType=Conversation',
|
'conversation': 'tag:'+domain+','+conversationDate+':objectId='+conversationId+':objectType=Conversation',
|
||||||
'content': content,
|
'content': content,
|
||||||
|
@ -290,11 +290,11 @@ def createPostBase(baseDir: str,username: str, domain: str, port: int, \
|
||||||
'attachment': [],
|
'attachment': [],
|
||||||
'tag': [],
|
'tag': [],
|
||||||
'replies': {}
|
'replies': {}
|
||||||
# 'id': 'https://'+domain+'/users/'+username+'/statuses/'+statusNumber+'/replies',
|
# 'id': 'https://'+domain+'/users/'+nickname+'/statuses/'+statusNumber+'/replies',
|
||||||
# 'type': 'Collection',
|
# 'type': 'Collection',
|
||||||
# 'first': {
|
# 'first': {
|
||||||
# 'type': 'CollectionPage',
|
# 'type': 'CollectionPage',
|
||||||
# 'partOf': 'https://'+domain+'/users/'+username+'/statuses/'+statusNumber+'/replies',
|
# 'partOf': 'https://'+domain+'/users/'+nickname+'/statuses/'+statusNumber+'/replies',
|
||||||
# 'items': []
|
# 'items': []
|
||||||
# }
|
# }
|
||||||
#}
|
#}
|
||||||
|
@ -307,13 +307,13 @@ def createPostBase(baseDir: str,username: str, domain: str, port: int, \
|
||||||
if saveToFile:
|
if saveToFile:
|
||||||
if ':' in domain:
|
if ':' in domain:
|
||||||
domain=domain.split(':')[0]
|
domain=domain.split(':')[0]
|
||||||
outboxDir = createOutboxDir(username,domain,baseDir)
|
outboxDir = createOutboxDir(nickname,domain,baseDir)
|
||||||
filename=outboxDir+'/'+newPostId.replace('/','#')+'.json'
|
filename=outboxDir+'/'+newPostId.replace('/','#')+'.json'
|
||||||
with open(filename, 'w') as fp:
|
with open(filename, 'w') as fp:
|
||||||
commentjson.dump(newPost, fp, indent=4, sort_keys=False)
|
commentjson.dump(newPost, fp, indent=4, sort_keys=False)
|
||||||
return newPost
|
return newPost
|
||||||
|
|
||||||
def createPublicPost(baseDir: str,username: str, domain: str, port: int,https: bool, \
|
def createPublicPost(baseDir: str,nickname: str, domain: str, port: int,https: bool, \
|
||||||
content: str, followersOnly: bool, saveToFile: bool, \
|
content: str, followersOnly: bool, saveToFile: bool, \
|
||||||
inReplyTo=None, inReplyToAtomUri=None, subject=None) -> {}:
|
inReplyTo=None, inReplyToAtomUri=None, subject=None) -> {}:
|
||||||
"""Public post to the outbox
|
"""Public post to the outbox
|
||||||
|
@ -321,9 +321,9 @@ def createPublicPost(baseDir: str,username: str, domain: str, port: int,https: b
|
||||||
prefix='https'
|
prefix='https'
|
||||||
if not https:
|
if not https:
|
||||||
prefix='http'
|
prefix='http'
|
||||||
return createPostBase(baseDir,username, domain, port, \
|
return createPostBase(baseDir,nickname, domain, port, \
|
||||||
'https://www.w3.org/ns/activitystreams#Public', \
|
'https://www.w3.org/ns/activitystreams#Public', \
|
||||||
prefix+'://'+domain+'/users/'+username+'/followers', \
|
prefix+'://'+domain+'/users/'+nickname+'/followers', \
|
||||||
https, content, followersOnly, saveToFile, \
|
https, content, followersOnly, saveToFile, \
|
||||||
inReplyTo, inReplyToAtomUri, subject)
|
inReplyTo, inReplyToAtomUri, subject)
|
||||||
|
|
||||||
|
@ -352,8 +352,8 @@ def threadSendPost(session,postJsonObject: {},federationList: [],inboxUrl: str,
|
||||||
time.sleep(backoffTime)
|
time.sleep(backoffTime)
|
||||||
backoffTime *= 2
|
backoffTime *= 2
|
||||||
|
|
||||||
def sendPost(session,baseDir: str,username: str, domain: str, port: int, \
|
def sendPost(session,baseDir: str,nickname: str, domain: str, port: int, \
|
||||||
toUsername: str, toDomain: str, toPort: int, cc: str, \
|
toNickname: str, toDomain: str, toPort: int, cc: str, \
|
||||||
https: bool, content: str, followersOnly: bool, \
|
https: bool, content: str, followersOnly: bool, \
|
||||||
saveToFile: bool, federationList: [], sendThreads: [], \
|
saveToFile: bool, federationList: [], sendThreads: [], \
|
||||||
postLog: [], cachedWebfingers: {},personCache: {}, \
|
postLog: [], cachedWebfingers: {},personCache: {}, \
|
||||||
|
@ -369,7 +369,7 @@ def sendPost(session,baseDir: str,username: str, domain: str, port: int, \
|
||||||
if toPort!=80 and toPort!=443:
|
if toPort!=80 and toPort!=443:
|
||||||
toDomain=toDomain+':'+str(toPort)
|
toDomain=toDomain+':'+str(toPort)
|
||||||
|
|
||||||
handle=prefix+'://'+toDomain+'/@'+toUsername
|
handle=prefix+'://'+toDomain+'/@'+toNickname
|
||||||
|
|
||||||
# lookup the inbox for the To handle
|
# lookup the inbox for the To handle
|
||||||
wfRequest = webfingerHandle(session,handle,https,cachedWebfingers)
|
wfRequest = webfingerHandle(session,handle,https,cachedWebfingers)
|
||||||
|
@ -386,20 +386,20 @@ def sendPost(session,baseDir: str,username: str, domain: str, port: int, \
|
||||||
if not toPersonId:
|
if not toPersonId:
|
||||||
return 4
|
return 4
|
||||||
|
|
||||||
postJsonObject=createPostBase(baseDir,username,domain,port, \
|
postJsonObject=createPostBase(baseDir,nickname,domain,port, \
|
||||||
toPersonId,cc,https,content, \
|
toPersonId,cc,https,content, \
|
||||||
followersOnly,saveToFile, \
|
followersOnly,saveToFile, \
|
||||||
inReplyTo,inReplyToAtomUri, \
|
inReplyTo,inReplyToAtomUri, \
|
||||||
subject)
|
subject)
|
||||||
|
|
||||||
# get the senders private key
|
# get the senders private key
|
||||||
privateKeyPem=getPersonKey(username,domain,baseDir,'private')
|
privateKeyPem=getPersonKey(nickname,domain,baseDir,'private')
|
||||||
if len(privateKeyPem)==0:
|
if len(privateKeyPem)==0:
|
||||||
return 5
|
return 5
|
||||||
|
|
||||||
# construct the http header
|
# construct the http header
|
||||||
signatureHeaderJson = \
|
signatureHeaderJson = \
|
||||||
createSignedHeader(privateKeyPem, username, domain, port, \
|
createSignedHeader(privateKeyPem, nickname, domain, port, \
|
||||||
'/inbox', https, withDigest, postJsonObject)
|
'/inbox', https, withDigest, postJsonObject)
|
||||||
|
|
||||||
# Keep the number of threads being used small
|
# Keep the number of threads being used small
|
||||||
|
@ -416,7 +416,7 @@ def sendPost(session,baseDir: str,username: str, domain: str, port: int, \
|
||||||
thr.start()
|
thr.start()
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def createOutbox(baseDir: str,username: str,domain: str,port: int,https: bool, \
|
def createOutbox(baseDir: str,nickname: str,domain: str,port: int,https: bool, \
|
||||||
itemsPerPage: int,headerOnly: bool,pageNumber=None) -> {}:
|
itemsPerPage: int,headerOnly: bool,pageNumber=None) -> {}:
|
||||||
"""Constructs the outbox feed
|
"""Constructs the outbox feed
|
||||||
"""
|
"""
|
||||||
|
@ -424,7 +424,7 @@ def createOutbox(baseDir: str,username: str,domain: str,port: int,https: bool, \
|
||||||
if not https:
|
if not https:
|
||||||
prefix='http'
|
prefix='http'
|
||||||
|
|
||||||
outboxDir = createOutboxDir(username,domain,baseDir)
|
outboxDir = createOutboxDir(nickname,domain,baseDir)
|
||||||
|
|
||||||
if port!=80 and port!=443:
|
if port!=80 and port!=443:
|
||||||
domain = domain+':'+str(port)
|
domain = domain+':'+str(port)
|
||||||
|
@ -436,16 +436,16 @@ def createOutbox(baseDir: str,username: str,domain: str,port: int,https: bool, \
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
outboxHeader = {'@context': 'https://www.w3.org/ns/activitystreams',
|
outboxHeader = {'@context': 'https://www.w3.org/ns/activitystreams',
|
||||||
'first': prefix+'://'+domain+'/users/'+username+'/outbox?page=true',
|
'first': prefix+'://'+domain+'/users/'+nickname+'/outbox?page=true',
|
||||||
'id': prefix+'://'+domain+'/users/'+username+'/outbox',
|
'id': prefix+'://'+domain+'/users/'+nickname+'/outbox',
|
||||||
'last': prefix+'://'+domain+'/users/'+username+'/outbox?page=true',
|
'last': prefix+'://'+domain+'/users/'+nickname+'/outbox?page=true',
|
||||||
'totalItems': 0,
|
'totalItems': 0,
|
||||||
'type': 'OrderedCollection'}
|
'type': 'OrderedCollection'}
|
||||||
outboxItems = {'@context': 'https://www.w3.org/ns/activitystreams',
|
outboxItems = {'@context': 'https://www.w3.org/ns/activitystreams',
|
||||||
'id': prefix+'://'+domain+'/users/'+username+'/outbox'+pageStr,
|
'id': prefix+'://'+domain+'/users/'+nickname+'/outbox'+pageStr,
|
||||||
'orderedItems': [
|
'orderedItems': [
|
||||||
],
|
],
|
||||||
'partOf': prefix+'://'+domain+'/users/'+username+'/outbox',
|
'partOf': prefix+'://'+domain+'/users/'+nickname+'/outbox',
|
||||||
'type': 'OrderedCollectionPage'}
|
'type': 'OrderedCollectionPage'}
|
||||||
|
|
||||||
# counter for posts loop
|
# counter for posts loop
|
||||||
|
@ -467,7 +467,7 @@ def createOutbox(baseDir: str,username: str,domain: str,port: int,https: bool, \
|
||||||
if lastPage<1:
|
if lastPage<1:
|
||||||
lastPage=1
|
lastPage=1
|
||||||
outboxHeader['last']= \
|
outboxHeader['last']= \
|
||||||
prefix+'://'+domain+'/users/'+username+'/outbox?page='+str(lastPage)
|
prefix+'://'+domain+'/users/'+nickname+'/outbox?page='+str(lastPage)
|
||||||
|
|
||||||
# Insert posts
|
# Insert posts
|
||||||
currPage=1
|
currPage=1
|
||||||
|
@ -478,7 +478,7 @@ def createOutbox(baseDir: str,username: str,domain: str,port: int,https: bool, \
|
||||||
# update the prev entry for the last message id
|
# update the prev entry for the last message id
|
||||||
postId = prevPostFilename.split('#statuses#')[1].replace('#activity','')
|
postId = prevPostFilename.split('#statuses#')[1].replace('#activity','')
|
||||||
outboxHeader['prev']= \
|
outboxHeader['prev']= \
|
||||||
prefix+'://'+domain+'/users/'+username+'/outbox?min_id='+postId+'&page=true'
|
prefix+'://'+domain+'/users/'+nickname+'/outbox?min_id='+postId+'&page=true'
|
||||||
# get the full path of the post file
|
# get the full path of the post file
|
||||||
filePath = os.path.join(outboxDir, postFilename)
|
filePath = os.path.join(outboxDir, postFilename)
|
||||||
try:
|
try:
|
||||||
|
@ -497,7 +497,7 @@ def createOutbox(baseDir: str,username: str,domain: str,port: int,https: bool, \
|
||||||
postId = p['id'].split('/statuses/')[1].replace('/activity','')
|
postId = p['id'].split('/statuses/')[1].replace('/activity','')
|
||||||
outboxHeader['next']= \
|
outboxHeader['next']= \
|
||||||
prefix+'://'+domain+'/users/'+ \
|
prefix+'://'+domain+'/users/'+ \
|
||||||
username+'/outbox?max_id='+ \
|
nickname+'/outbox?max_id='+ \
|
||||||
postId+'&page=true'
|
postId+'&page=true'
|
||||||
postsOnPageCtr += 1
|
postsOnPageCtr += 1
|
||||||
# remember the last post filename for use with prev
|
# remember the last post filename for use with prev
|
||||||
|
@ -515,13 +515,13 @@ def createOutbox(baseDir: str,username: str,domain: str,port: int,https: bool, \
|
||||||
return outboxHeader
|
return outboxHeader
|
||||||
return outboxItems
|
return outboxItems
|
||||||
|
|
||||||
def archivePosts(username: str,domain: str,baseDir: str, \
|
def archivePosts(nickname: str,domain: str,baseDir: str, \
|
||||||
maxPostsInOutbox=256) -> None:
|
maxPostsInOutbox=256) -> None:
|
||||||
"""Retain a maximum number of posts within the outbox
|
"""Retain a maximum number of posts within the outbox
|
||||||
Move any others to an archive directory
|
Move any others to an archive directory
|
||||||
"""
|
"""
|
||||||
outboxDir = createOutboxDir(username,domain,baseDir)
|
outboxDir = createOutboxDir(nickname,domain,baseDir)
|
||||||
archiveDir = createOutboxArchive(username,domain,baseDir)
|
archiveDir = createOutboxArchive(nickname,domain,baseDir)
|
||||||
postsInOutbox=sorted(os.listdir(outboxDir), reverse=False)
|
postsInOutbox=sorted(os.listdir(outboxDir), reverse=False)
|
||||||
noOfPosts=len(postsInOutbox)
|
noOfPosts=len(postsInOutbox)
|
||||||
if noOfPosts<=maxPostsInOutbox:
|
if noOfPosts<=maxPostsInOutbox:
|
||||||
|
|
72
tests.py
72
tests.py
|
@ -35,12 +35,12 @@ testServerBobRunning = False
|
||||||
|
|
||||||
def testHttpsigBase(withDigest):
|
def testHttpsigBase(withDigest):
|
||||||
print('testHttpsig(' + str(withDigest) + ')')
|
print('testHttpsig(' + str(withDigest) + ')')
|
||||||
username='socrates'
|
nickname='socrates'
|
||||||
domain='argumentative.social'
|
domain='argumentative.social'
|
||||||
https=True
|
https=True
|
||||||
port=5576
|
port=5576
|
||||||
baseDir=os.getcwd()
|
baseDir=os.getcwd()
|
||||||
privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(baseDir,username,domain,port,https,False)
|
privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(baseDir,nickname,domain,port,https,False)
|
||||||
messageBodyJsonStr = '{"a key": "a value", "another key": "A string"}'
|
messageBodyJsonStr = '{"a key": "a value", "another key": "A string"}'
|
||||||
|
|
||||||
headersDomain=domain
|
headersDomain=domain
|
||||||
|
@ -54,7 +54,7 @@ def testHttpsigBase(withDigest):
|
||||||
headers = {'host': headersDomain, 'digest': f'SHA-256={bodyDigest}'}
|
headers = {'host': headersDomain, 'digest': f'SHA-256={bodyDigest}'}
|
||||||
|
|
||||||
path='/inbox'
|
path='/inbox'
|
||||||
signatureHeader = signPostHeaders(privateKeyPem, username, domain, port, path, https, None)
|
signatureHeader = signPostHeaders(privateKeyPem, nickname, domain, port, path, https, None)
|
||||||
headers['signature'] = signatureHeader
|
headers['signature'] = signatureHeader
|
||||||
assert verifyPostHeaders(https, publicKeyPem, headers, '/inbox' ,False, messageBodyJsonStr)
|
assert verifyPostHeaders(https, publicKeyPem, headers, '/inbox' ,False, messageBodyJsonStr)
|
||||||
assert verifyPostHeaders(https, publicKeyPem, headers, '/parambulator/inbox', False , messageBodyJsonStr) == False
|
assert verifyPostHeaders(https, publicKeyPem, headers, '/parambulator/inbox', False , messageBodyJsonStr) == False
|
||||||
|
@ -104,16 +104,16 @@ def createServerAlice(path: str,domain: str,port: int,federationList: []):
|
||||||
shutil.rmtree(path)
|
shutil.rmtree(path)
|
||||||
os.mkdir(path)
|
os.mkdir(path)
|
||||||
os.chdir(path)
|
os.chdir(path)
|
||||||
username='alice'
|
nickname='alice'
|
||||||
https=False
|
https=False
|
||||||
useTor=False
|
useTor=False
|
||||||
privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(path,username,domain,port,https,True)
|
privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(path,nickname,domain,port,https,True)
|
||||||
deleteAllPosts(username,domain,path)
|
deleteAllPosts(nickname,domain,path)
|
||||||
followPerson(path,username,domain,'bob','127.0.0.100:61936',federationList)
|
followPerson(path,nickname,domain,'bob','127.0.0.100:61936',federationList)
|
||||||
followerOfPerson(path,username,domain,'bob','127.0.0.100:61936',federationList)
|
followerOfPerson(path,nickname,domain,'bob','127.0.0.100:61936',federationList)
|
||||||
createPublicPost(path,username, domain, port,https, "No wise fish would go anywhere without a porpoise", False, True)
|
createPublicPost(path,nickname, 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,nickname, 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)
|
createPublicPost(path,nickname, domain, port,https, "In the gardens of memory, in the palace of dreams, that is where you and I shall meet", False, True)
|
||||||
global testServerAliceRunning
|
global testServerAliceRunning
|
||||||
testServerAliceRunning = True
|
testServerAliceRunning = True
|
||||||
print('Server running: Alice')
|
print('Server running: Alice')
|
||||||
|
@ -125,16 +125,16 @@ def createServerBob(path: str,domain: str,port: int,federationList: []):
|
||||||
shutil.rmtree(path)
|
shutil.rmtree(path)
|
||||||
os.mkdir(path)
|
os.mkdir(path)
|
||||||
os.chdir(path)
|
os.chdir(path)
|
||||||
username='bob'
|
nickname='bob'
|
||||||
https=False
|
https=False
|
||||||
useTor=False
|
useTor=False
|
||||||
privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(path,username,domain,port,https,True)
|
privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(path,nickname,domain,port,https,True)
|
||||||
deleteAllPosts(username,domain,path)
|
deleteAllPosts(nickname,domain,path)
|
||||||
followPerson(path,username,domain,'alice','127.0.0.50:61935',federationList)
|
followPerson(path,nickname,domain,'alice','127.0.0.50:61935',federationList)
|
||||||
followerOfPerson(path,username,domain,'alice','127.0.0.50:61935',federationList)
|
followerOfPerson(path,nickname,domain,'alice','127.0.0.50:61935',federationList)
|
||||||
createPublicPost(path,username, domain, port,https, "It's your life, live it your way.", False, True)
|
createPublicPost(path,nickname, 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,nickname, 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)
|
createPublicPost(path,nickname, domain, port,https, "Quantum physics is a bit of a passion of mine", False, True)
|
||||||
global testServerBobRunning
|
global testServerBobRunning
|
||||||
testServerBobRunning = True
|
testServerBobRunning = True
|
||||||
print('Server running: Bob')
|
print('Server running: Bob')
|
||||||
|
@ -208,7 +208,7 @@ def testPostMessageBetweenServers():
|
||||||
|
|
||||||
def testFollows():
|
def testFollows():
|
||||||
currDir=os.getcwd()
|
currDir=os.getcwd()
|
||||||
username='test529'
|
nickname='test529'
|
||||||
domain='testdomain.com'
|
domain='testdomain.com'
|
||||||
port=80
|
port=80
|
||||||
https=True
|
https=True
|
||||||
|
@ -218,16 +218,16 @@ def testFollows():
|
||||||
shutil.rmtree(baseDir)
|
shutil.rmtree(baseDir)
|
||||||
os.mkdir(baseDir)
|
os.mkdir(baseDir)
|
||||||
os.chdir(baseDir)
|
os.chdir(baseDir)
|
||||||
createPerson(baseDir,username,domain,port,https,True)
|
createPerson(baseDir,nickname,domain,port,https,True)
|
||||||
|
|
||||||
clearFollows(baseDir,username,domain)
|
clearFollows(baseDir,nickname,domain)
|
||||||
followPerson(baseDir,username,domain,'badger','wild.com',federationList)
|
followPerson(baseDir,nickname,domain,'badger','wild.com',federationList)
|
||||||
followPerson(baseDir,username,domain,'squirrel','secret.com',federationList)
|
followPerson(baseDir,nickname,domain,'squirrel','secret.com',federationList)
|
||||||
followPerson(baseDir,username,domain,'rodent','drainpipe.com',federationList)
|
followPerson(baseDir,nickname,domain,'rodent','drainpipe.com',federationList)
|
||||||
followPerson(baseDir,username,domain,'batman','mesh.com',federationList)
|
followPerson(baseDir,nickname,domain,'batman','mesh.com',federationList)
|
||||||
followPerson(baseDir,username,domain,'giraffe','trees.com',federationList)
|
followPerson(baseDir,nickname,domain,'giraffe','trees.com',federationList)
|
||||||
|
|
||||||
f = open(baseDir+'/accounts/'+username+'@'+domain+'/following.txt', "r")
|
f = open(baseDir+'/accounts/'+nickname+'@'+domain+'/following.txt', "r")
|
||||||
domainFound=False
|
domainFound=False
|
||||||
for followingDomain in f:
|
for followingDomain in f:
|
||||||
testDomain=followingDomain.split('@')[1].replace('\n','')
|
testDomain=followingDomain.split('@')[1].replace('\n','')
|
||||||
|
@ -238,7 +238,7 @@ def testFollows():
|
||||||
assert(False)
|
assert(False)
|
||||||
|
|
||||||
assert(domainFound)
|
assert(domainFound)
|
||||||
unfollowPerson(baseDir,username,domain,'batman','mesh.com')
|
unfollowPerson(baseDir,nickname,domain,'batman','mesh.com')
|
||||||
|
|
||||||
domainFound=False
|
domainFound=False
|
||||||
for followingDomain in f:
|
for followingDomain in f:
|
||||||
|
@ -247,14 +247,14 @@ def testFollows():
|
||||||
domainFound=True
|
domainFound=True
|
||||||
assert(domainFound==False)
|
assert(domainFound==False)
|
||||||
|
|
||||||
clearFollowers(baseDir,username,domain)
|
clearFollowers(baseDir,nickname,domain)
|
||||||
followerOfPerson(baseDir,username,domain,'badger','wild.com',federationList)
|
followerOfPerson(baseDir,nickname,domain,'badger','wild.com',federationList)
|
||||||
followerOfPerson(baseDir,username,domain,'squirrel','secret.com',federationList)
|
followerOfPerson(baseDir,nickname,domain,'squirrel','secret.com',federationList)
|
||||||
followerOfPerson(baseDir,username,domain,'rodent','drainpipe.com',federationList)
|
followerOfPerson(baseDir,nickname,domain,'rodent','drainpipe.com',federationList)
|
||||||
followerOfPerson(baseDir,username,domain,'batman','mesh.com',federationList)
|
followerOfPerson(baseDir,nickname,domain,'batman','mesh.com',federationList)
|
||||||
followerOfPerson(baseDir,username,domain,'giraffe','trees.com',federationList)
|
followerOfPerson(baseDir,nickname,domain,'giraffe','trees.com',federationList)
|
||||||
|
|
||||||
f = open(baseDir+'/accounts/'+username+'@'+domain+'/followers.txt', "r")
|
f = open(baseDir+'/accounts/'+nickname+'@'+domain+'/followers.txt', "r")
|
||||||
for followerDomain in f:
|
for followerDomain in f:
|
||||||
testDomain=followerDomain.split('@')[1].replace('\n','')
|
testDomain=followerDomain.split('@')[1].replace('\n','')
|
||||||
if testDomain not in federationList:
|
if testDomain not in federationList:
|
||||||
|
|
4
utils.py
4
utils.py
|
@ -20,10 +20,10 @@ def getStatusNumber() -> (str,str):
|
||||||
conversationDate=currTime.strftime("%Y-%m-%d")
|
conversationDate=currTime.strftime("%Y-%m-%d")
|
||||||
return statusNumber,published
|
return statusNumber,published
|
||||||
|
|
||||||
def createOutboxDir(username: str,domain: str,baseDir: str) -> str:
|
def createOutboxDir(nickname: str,domain: str,baseDir: str) -> str:
|
||||||
"""Create an outbox for a person and returns the feed filename and directory
|
"""Create an outbox for a person and returns the feed filename and directory
|
||||||
"""
|
"""
|
||||||
handle=username.lower()+'@'+domain.lower()
|
handle=nickname.lower()+'@'+domain.lower()
|
||||||
if not os.path.isdir(baseDir+'/accounts/'+handle):
|
if not os.path.isdir(baseDir+'/accounts/'+handle):
|
||||||
os.mkdir(baseDir+'/accounts/'+handle)
|
os.mkdir(baseDir+'/accounts/'+handle)
|
||||||
outboxDir=baseDir+'/accounts/'+handle+'/outbox'
|
outboxDir=baseDir+'/accounts/'+handle+'/outbox'
|
||||||
|
|
36
webfinger.py
36
webfinger.py
|
@ -21,43 +21,43 @@ def parseHandle(handle: str) -> (str,str):
|
||||||
if '.' not in handle:
|
if '.' not in handle:
|
||||||
return None, None
|
return None, None
|
||||||
if '/@' in handle:
|
if '/@' in handle:
|
||||||
domain, username = \
|
domain, nickname = \
|
||||||
handle.replace('https://','').replace('http://','').split('/@')
|
handle.replace('https://','').replace('http://','').split('/@')
|
||||||
else:
|
else:
|
||||||
if '/users/' in handle:
|
if '/users/' in handle:
|
||||||
domain, username = \
|
domain, nickname = \
|
||||||
handle.replace('https://','').replace('http://','').split('/users/')
|
handle.replace('https://','').replace('http://','').split('/users/')
|
||||||
else:
|
else:
|
||||||
if '@' in handle:
|
if '@' in handle:
|
||||||
username, domain = handle.split('@')
|
nickname, domain = handle.split('@')
|
||||||
else:
|
else:
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
return username, domain
|
return nickname, domain
|
||||||
|
|
||||||
|
|
||||||
def webfingerHandle(session,handle: str,https: bool,cachedWebfingers: {}) -> {}:
|
def webfingerHandle(session,handle: str,https: bool,cachedWebfingers: {}) -> {}:
|
||||||
username, domain = parseHandle(handle)
|
nickname, domain = parseHandle(handle)
|
||||||
if not username:
|
if not nickname:
|
||||||
return None
|
return None
|
||||||
wfDomain=domain
|
wfDomain=domain
|
||||||
if ':' in wfDomain:
|
if ':' in wfDomain:
|
||||||
wfDomain=wfDomain.split(':')[0]
|
wfDomain=wfDomain.split(':')[0]
|
||||||
print('***********cachedWebfingers '+str(cachedWebfingers))
|
print('***********cachedWebfingers '+str(cachedWebfingers))
|
||||||
wf=getWebfingerFromCache(username+'@'+wfDomain,cachedWebfingers)
|
wf=getWebfingerFromCache(nickname+'@'+wfDomain,cachedWebfingers)
|
||||||
if wf:
|
if wf:
|
||||||
return wf
|
return wf
|
||||||
prefix='https'
|
prefix='https'
|
||||||
if not https:
|
if not https:
|
||||||
prefix='http'
|
prefix='http'
|
||||||
url = '{}://{}/.well-known/webfinger'.format(prefix,domain)
|
url = '{}://{}/.well-known/webfinger'.format(prefix,domain)
|
||||||
par = {'resource': 'acct:{}'.format(username+'@'+wfDomain)}
|
par = {'resource': 'acct:{}'.format(nickname+'@'+wfDomain)}
|
||||||
hdr = {'Accept': 'application/jrd+json'}
|
hdr = {'Accept': 'application/jrd+json'}
|
||||||
#try:
|
#try:
|
||||||
result = getJson(session, url, hdr, par)
|
result = getJson(session, url, hdr, par)
|
||||||
#except:
|
#except:
|
||||||
# print("Unable to webfinger " + url + ' ' + str(hdr) + ' ' + str(par))
|
# print("Unable to webfinger " + url + ' ' + str(hdr) + ' ' + str(par))
|
||||||
storeWebfingerInCache(username+'@'+wfDomain,result,cachedWebfingers)
|
storeWebfingerInCache(nickname+'@'+wfDomain,result,cachedWebfingers)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def generateMagicKey(publicKeyPem) -> str:
|
def generateMagicKey(publicKeyPem) -> str:
|
||||||
|
@ -69,11 +69,11 @@ def generateMagicKey(publicKeyPem) -> str:
|
||||||
pubexp = base64.urlsafe_b64encode(number.long_to_bytes(privkey.e)).decode("utf-8")
|
pubexp = base64.urlsafe_b64encode(number.long_to_bytes(privkey.e)).decode("utf-8")
|
||||||
return f"data:application/magic-public-key,RSA.{mod}.{pubexp}"
|
return f"data:application/magic-public-key,RSA.{mod}.{pubexp}"
|
||||||
|
|
||||||
def storeWebfingerEndpoint(username: str,domain: str,baseDir: str, \
|
def storeWebfingerEndpoint(nickname: str,domain: str,baseDir: str, \
|
||||||
wfJson: {}) -> bool:
|
wfJson: {}) -> bool:
|
||||||
"""Stores webfinger endpoint for a user to a file
|
"""Stores webfinger endpoint for a user to a file
|
||||||
"""
|
"""
|
||||||
handle=username+'@'+domain
|
handle=nickname+'@'+domain
|
||||||
wfSubdir='/wfendpoints'
|
wfSubdir='/wfendpoints'
|
||||||
if not os.path.isdir(baseDir+wfSubdir):
|
if not os.path.isdir(baseDir+wfSubdir):
|
||||||
os.mkdir(baseDir+wfSubdir)
|
os.mkdir(baseDir+wfSubdir)
|
||||||
|
@ -82,7 +82,7 @@ def storeWebfingerEndpoint(username: str,domain: str,baseDir: str, \
|
||||||
commentjson.dump(wfJson, fp, indent=4, sort_keys=False)
|
commentjson.dump(wfJson, fp, indent=4, sort_keys=False)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def createWebfingerEndpoint(username: str,domain: str,port: int, \
|
def createWebfingerEndpoint(nickname: str,domain: str,port: int, \
|
||||||
https: bool,publicKeyPem) -> {}:
|
https: bool,publicKeyPem) -> {}:
|
||||||
"""Creates a webfinger endpoint for a user
|
"""Creates a webfinger endpoint for a user
|
||||||
"""
|
"""
|
||||||
|
@ -95,22 +95,22 @@ def createWebfingerEndpoint(username: str,domain: str,port: int, \
|
||||||
|
|
||||||
account = {
|
account = {
|
||||||
"aliases": [
|
"aliases": [
|
||||||
prefix+"://"+domain+"/@"+username,
|
prefix+"://"+domain+"/@"+nickname,
|
||||||
prefix+"://"+domain+"/users/"+username
|
prefix+"://"+domain+"/users/"+nickname
|
||||||
],
|
],
|
||||||
"links": [
|
"links": [
|
||||||
{
|
{
|
||||||
"href": prefix+"://"+domain+"/@"+username,
|
"href": prefix+"://"+domain+"/@"+nickname,
|
||||||
"rel": "http://webfinger.net/rel/profile-page",
|
"rel": "http://webfinger.net/rel/profile-page",
|
||||||
"type": "text/html"
|
"type": "text/html"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"href": prefix+"://"+domain+"/users/"+username+".atom",
|
"href": prefix+"://"+domain+"/users/"+nickname+".atom",
|
||||||
"rel": "http://schemas.google.com/g/2010#updates-from",
|
"rel": "http://schemas.google.com/g/2010#updates-from",
|
||||||
"type": "application/atom+xml"
|
"type": "application/atom+xml"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"href": prefix+"://"+domain+"/users/"+username,
|
"href": prefix+"://"+domain+"/users/"+nickname,
|
||||||
"rel": "self",
|
"rel": "self",
|
||||||
"type": "application/activity+json"
|
"type": "application/activity+json"
|
||||||
},
|
},
|
||||||
|
@ -127,7 +127,7 @@ def createWebfingerEndpoint(username: str,domain: str,port: int, \
|
||||||
"template": prefix+"://"+domain+"/authorize_interaction?uri={uri}"
|
"template": prefix+"://"+domain+"/authorize_interaction?uri={uri}"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"subject": "acct:"+username+"@"+domain
|
"subject": "acct:"+nickname+"@"+domain
|
||||||
}
|
}
|
||||||
return account
|
return account
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue