diff --git a/acceptreject.py b/acceptreject.py index ac508580..69b32add 100644 --- a/acceptreject.py +++ b/acceptreject.py @@ -12,7 +12,7 @@ from utils import getStatusNumber from utils import createOutboxDir from utils import urlPermitted -def createAcceptReject(baseDir: str,federationList: [],nickname: 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,httpPrefix: str,objectUrl: str,acceptType: str) -> {}: """Accepts or rejects something (eg. a follow request) 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 @@ -21,16 +21,12 @@ def createAcceptReject(baseDir: str,federationList: [],nickname: str,domain: str if not urlPermitted(objectUrl,federationList): return None - prefix='https' - if not https: - prefix='http' - if port!=80 and port!=443: domain=domain+':'+str(port) newAccept = { 'type': acceptType, - 'actor': prefix+'://'+domain+'/users/'+nickname, + 'actor': httpPrefix+'://'+domain+'/users/'+nickname, 'to': [toUrl], 'cc': [], 'object': objectUrl @@ -40,8 +36,8 @@ def createAcceptReject(baseDir: str,federationList: [],nickname: str,domain: str newAccept['cc']=ccUrl return newAccept -def createAccept(baseDir: str,federationList: [],nickname: str,domain: str,port: int,toUrl: str,ccUrl: str,https: bool,objectUrl: str) -> {}: - return createAcceptReject(baseDir,federationList,nickname,domain,port,toUrl,ccUrl,https,objectUrl,'Accept') +def createAccept(baseDir: str,federationList: [],nickname: str,domain: str,port: int,toUrl: str,ccUrl: str,httpPrefix: str,objectUrl: str) -> {}: + return createAcceptReject(baseDir,federationList,nickname,domain,port,toUrl,ccUrl,httpPrefix,objectUrl,'Accept') -def createReject(baseDir: str,federationList: [],nickname: str,domain: str,port: int,toUrl: str,ccUrl: str,https: bool,objectUrl: str) -> {}: - return createAcceptReject(baseDir,federationList,nickname,domain,port,toUrl,ccUrl,https,objectUrl,'Reject') +def createReject(baseDir: str,federationList: [],nickname: str,domain: str,port: int,toUrl: str,ccUrl: str,httpPrefix: str,objectUrl: str) -> {}: + return createAcceptReject(baseDir,federationList,nickname,domain,port,toUrl,ccUrl,httpPrefix,objectUrl,'Reject') diff --git a/announce.py b/announce.py index cddec0bd..a639173b 100644 --- a/announce.py +++ b/announce.py @@ -14,7 +14,7 @@ from utils import urlPermitted def createAnnounce(baseDir: str,federationList: [], \ nickname: str, domain: str, port: int, \ - toUrl: str, ccUrl: str, https: bool, \ + toUrl: str, ccUrl: str, httpPrefix: str, \ objectUrl: str, saveToFile: bool) -> {}: """Creates an announce message Typically toUrl will be https://www.w3.org/ns/activitystreams#Public @@ -24,18 +24,14 @@ def createAnnounce(baseDir: str,federationList: [], \ if not urlPermitted(objectUrl,federationList): return None - prefix='https' - if not https: - prefix='http' - if port!=80 and port!=443: domain=domain+':'+str(port) statusNumber,published = getStatusNumber() - newAnnounceId=prefix+'://'+domain+'/users/'+nickname+'/statuses/'+statusNumber + newAnnounceId=httpPrefix+'://'+domain+'/users/'+nickname+'/statuses/'+statusNumber newAnnounce = { - 'actor': prefix+'://'+domain+'/users/'+nickname, - 'atomUri': prefix+'://'+domain+'/users/'+nickname+'/statuses/'+statusNumber, + 'actor': httpPrefix+'://'+domain+'/users/'+nickname, + 'atomUri': httpPrefix+'://'+domain+'/users/'+nickname+'/statuses/'+statusNumber, 'cc': [], 'id': newAnnounceId+'/activity', 'object': objectUrl, @@ -56,40 +52,32 @@ def createAnnounce(baseDir: str,federationList: [], \ return newAnnounce def announcePublic(baseDir: str,federationList: [], \ - nickname: str, domain: str, port: int, https: bool, \ + nickname: str, domain: str, port: int, httpPrefix: str, \ objectUrl: str, saveToFile: bool) -> {}: """Makes a public announcement """ - prefix='https' - if not https: - prefix='http' - fromDomain=domain if port!=80 and port!=443: fromDomain=fromDomain+':'+str(port) toUrl = 'https://www.w3.org/ns/activitystreams#Public' - ccUrl = prefix + '://'+fromDomain+'/users/'+nickname+'/followers' + ccUrl = httpPrefix + '://'+fromDomain+'/users/'+nickname+'/followers' return createAnnounce(baseDir,nickname, domain, port, \ - toUrl, ccUrl, https, objectUrl, saveToFile) + toUrl, ccUrl, httpPrefix, objectUrl, saveToFile) def repeatPost(baseDir: str,federationList: [], \ - nickname: str, domain: str, port: int, https: bool, \ + nickname: str, domain: str, port: int, httpPrefix: str, \ announceNickname: str, announceDomain: str, \ - announcePort: int, announceHttps: bool, \ + announcePort: int, announceHttpsPrefix: str, \ announceStatusNumber: int, saveToFile: bool) -> {}: """Repeats a given status post """ - prefix='https' - if not announceHttps: - prefix='http' - announcedDomain=announceDomain if announcePort!=80 and announcePort!=443: announcedDomain=announcedDomain+':'+str(announcePort) - objectUrl = prefix + '://'+announcedDomain+'/users/'+ \ + objectUrl = announceHttpsPrefix + '://'+announcedDomain+'/users/'+ \ announceNickname+'/statuses/'+str(announceStatusNumber) - return announcePublic(baseDir,nickname, domain, port, https, objectUrl, saveToFile) + return announcePublic(baseDir,nickname, domain, port, httpPrefix, objectUrl, saveToFile) diff --git a/daemon.py b/daemon.py index e3a2eb06..f98d04eb 100644 --- a/daemon.py +++ b/daemon.py @@ -129,7 +129,7 @@ class PubServer(BaseHTTPRequestHandler): # get outbox feed for a person outboxFeed=personOutboxJson(self.server.baseDir,self.server.domain, \ self.server.port,self.path, \ - self.server.https,maxPostsInFeed) + self.server.httpPrefix,maxPostsInFeed) if outboxFeed: self._set_headers('application/json') self.wfile.write(json.dumps(outboxFeed).encode('utf-8')) @@ -137,7 +137,7 @@ class PubServer(BaseHTTPRequestHandler): return following=getFollowingFeed(self.server.baseDir,self.server.domain, \ self.server.port,self.path, \ - self.server.https,followsPerPage) + self.server.httpPrefix,followsPerPage) if following: self._set_headers('application/json') self.wfile.write(json.dumps(following).encode('utf-8')) @@ -145,7 +145,7 @@ class PubServer(BaseHTTPRequestHandler): return followers=getFollowingFeed(self.server.baseDir,self.server.domain, \ self.server.port,self.path, \ - self.server.https,followsPerPage,'followers') + self.server.httpPrefix,followsPerPage,'followers') if followers: self._set_headers('application/json') self.wfile.write(json.dumps(followers).encode('utf-8')) @@ -305,7 +305,7 @@ class PubServer(BaseHTTPRequestHandler): if self.server.debug: print('DEBUG: POST check signature') - if not verifyPostHeaders(self.server.https, pubKey, self.headers, \ + if not verifyPostHeaders(self.server.httpPrefix, pubKey, self.headers, \ '/inbox' ,False, json.dumps(messageJson)): print('**************** POST signature verification failed') self.send_response(401) @@ -336,7 +336,7 @@ class PubServer(BaseHTTPRequestHandler): self.end_headers() self.server.POSTbusy=False -def runDaemon(domain: str,port=80,https=True,fedList=[],useTor=False,debug=False) -> None: +def runDaemon(domain: str,port=80,httpPrefix='https',fedList=[],useTor=False,debug=False) -> None: if len(domain)==0: domain='localhost' if '.' not in domain: @@ -348,7 +348,7 @@ def runDaemon(domain: str,port=80,https=True,fedList=[],useTor=False,debug=False httpd = ThreadingHTTPServer(serverAddress, PubServer) httpd.domain=domain httpd.port=port - httpd.https=https + httpd.httpPrefix=httpPrefix httpd.debug=debug httpd.federationList=fedList.copy() httpd.baseDir=os.getcwd() diff --git a/epicyon.py b/epicyon.py index 3613aee1..a99a9256 100644 --- a/epicyon.py +++ b/epicyon.py @@ -67,6 +67,9 @@ parser.add_argument("--debug", type=str2bool, nargs='?', parser.add_argument("--http", type=str2bool, nargs='?', const=True, default=False, help="Use http only") +parser.add_argument("--dat", type=str2bool, nargs='?', + const=True, default=False, + help="Use dat protocol only") parser.add_argument("--tor", type=str2bool, nargs='?', const=True, default=False, help="Route via Tor") @@ -110,9 +113,11 @@ if not args.domain: nickname='admin' domain=args.domain port=args.port -https=True +httpPrefix='https' if args.http: - https=False + httpPrefix='http' +if args.dat: + httpPrefix='dat' useTor=args.tor baseDir=args.baseDir if baseDir.endswith('/'): @@ -126,6 +131,6 @@ if args.federationList: if not os.path.isdir(baseDir+'/accounts/'+nickname+'@'+domain): print('Creating default admin account '+nickname+'@'+domain) - privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(baseDir,nickname,domain,port,https,True) + privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(baseDir,nickname,domain,port,httpPrefix,True) -runDaemon(domain,port,https,federationList,useTor,debug) +runDaemon(domain,port,httpPrefix,federationList,useTor,debug) diff --git a/follow.py b/follow.py index e25a6807..537e7742 100644 --- a/follow.py +++ b/follow.py @@ -114,7 +114,7 @@ def getNoOfFollowers(baseDir: str,nickname: str,domain: str) -> int: """ 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,httpPrefix: str, \ followsPerPage=12,followFile='following') -> {}: """Returns the following and followers feeds from GET requests """ @@ -147,18 +147,14 @@ def getFollowingFeed(baseDir: str,domain: str,port: int,path: str,https: bool, \ if not validNickname(nickname): return None - prefix='https' - if not https: - prefix='http' - if port!=80 and port!=443: domain=domain+':'+str(port) if headerOnly: following = { '@context': 'https://www.w3.org/ns/activitystreams', - 'first': prefix+'://'+domain+'/users/'+nickname+'/'+followFile+'?page=1', - 'id': prefix+'://'+domain+'/users/'+nickname+'/'+followFile, + 'first': httpPrefix+'://'+domain+'/users/'+nickname+'/'+followFile+'?page=1', + 'id': httpPrefix+'://'+domain+'/users/'+nickname+'/'+followFile, 'totalItems': getNoOfFollows(nickname,domain), 'type': 'OrderedCollection'} return following @@ -169,9 +165,9 @@ def getFollowingFeed(baseDir: str,domain: str,port: int,path: str,https: bool, \ nextPageNumber=int(pageNumber+1) following = { '@context': 'https://www.w3.org/ns/activitystreams', - 'id': prefix+'://'+domain+'/users/'+nickname+'/'+followFile+'?page='+str(pageNumber), + 'id': httpPrefix+'://'+domain+'/users/'+nickname+'/'+followFile+'?page='+str(pageNumber), 'orderedItems': [], - 'partOf': prefix+'://'+domain+'/users/'+nickname+'/'+followFile, + 'partOf': httpPrefix+'://'+domain+'/users/'+nickname+'/'+followFile, 'totalItems': 0, 'type': 'OrderedCollectionPage'} @@ -190,10 +186,10 @@ def getFollowingFeed(baseDir: str,domain: str,port: int,path: str,https: bool, \ pageCtr += 1 totalCtr += 1 if currPage==pageNumber: - url = prefix + '://' + line.lower().replace('\n','').split('@')[1] + \ + url = httpPrefix + '://' + line.lower().replace('\n','').split('@')[1] + \ '/users/' + line.lower().replace('\n','').split('@')[0] following['orderedItems'].append(url) - elif line.startswith('http') and '/users/' in line: + elif (line.startswith('http') or line.startswith('dat')) and '/users/' in line: pageCtr += 1 totalCtr += 1 if currPage==pageNumber: @@ -206,7 +202,7 @@ def getFollowingFeed(baseDir: str,domain: str,port: int,path: str,https: bool, \ if lastPage<1: lastPage=1 if nextPageNumber>lastPage: - following['next']=prefix+'://'+domain+'/users/'+nickname+'/'+followFile+'?page='+str(lastPage) + following['next']=httpPrefix+'://'+domain+'/users/'+nickname+'/'+followFile+'?page='+str(lastPage) return following def receiveFollowRequest(baseDir: str,messageJson: {},federationList: []) -> bool: @@ -216,7 +212,7 @@ def receiveFollowRequest(baseDir: str,messageJson: {},federationList: []) -> boo return False if '/users/' not in messageJson['actor']: return False - domain=messageJson['actor'].split('/users/')[0].replace('https://','').replace('http://','') + domain=messageJson['actor'].split('/users/')[0].replace('https://','').replace('http://','').replace('dat://','') if not domainPermitted(domain,federationList): return False nickname=messageJson['actor'].split('/users/')[1].replace('@','') @@ -225,7 +221,7 @@ def receiveFollowRequest(baseDir: str,messageJson: {},federationList: []) -> boo return False if '/users/' not in messageJson['object']: return False - domainToFollow=messageJson['object'].split('/users/')[0].replace('https://','').replace('http://','') + domainToFollow=messageJson['object'].split('/users/')[0].replace('https://','').replace('http://','').replace('dat://','') if not domainPermitted(domainToFollow,federationList): return False nicknameToFollow=messageJson['object'].split('/users/')[1].replace('@','') @@ -235,22 +231,14 @@ def receiveFollowRequest(baseDir: str,messageJson: {},federationList: []) -> boo return False return followerOfPerson(baseDir,nickname,domain,nicknameToFollow,domainToFollow,federationList) -def sendFollowRequest(baseDir: str,nickname: str,domain: str,port: int,https: bool, \ - followNickname: str,followDomain: str,followPort: bool,followHttps: bool, \ +def sendFollowRequest(baseDir: str,nickname: str,domain: str,port: int,httpPrefix: str, \ + followNickname: str,followDomain: str,followPort: bool,followHttpPrefix: str, \ federationList: []) -> {}: """Gets the json object for sending a follow request """ if not domainPermitted(followDomain,federationList): return None - prefix='https' - if not https: - prefix='http' - - followPrefix='https' - if not followHttps: - followPrefix='http' - if port!=80 and port!=443: domain=domain+':'+str(port) @@ -259,8 +247,8 @@ def sendFollowRequest(baseDir: str,nickname: str,domain: str,port: int,https: bo newFollow = { 'type': 'Follow', - 'actor': prefix+'://'+domain+'/users/'+nickname, - 'object': followPrefix+'://'+followDomain+'/users/'+followNickname, + 'actor': httpPrefix+'://'+domain+'/users/'+nickname, + 'object': followHttpPrefix+'://'+followDomain+'/users/'+followNickname, 'to': [toUrl], 'cc': [] } diff --git a/httpsig.py b/httpsig.py index b1695c1a..46da19cc 100644 --- a/httpsig.py +++ b/httpsig.py @@ -17,18 +17,14 @@ import json def signPostHeaders(privateKeyPem: str, nickname: str, domain: str, \ port: int,path: str, \ - https: bool, messageBodyJson: {}) -> str: + httpPrefix: str, messageBodyJson: {}) -> str: """Returns a raw signature string that can be plugged into a header and used to verify the authenticity of an HTTP transmission. """ - prefix='https' - if not https: - prefix='http' - if port!=80 and port!=443: domain=domain+':'+str(port) - keyID = prefix+'://'+domain+'/users/'+nickname+'/main-key' + keyID = httpPrefix+'://'+domain+'/users/'+nickname+'/main-key' if not messageBodyJson: headers = {'host': domain} else: @@ -63,7 +59,7 @@ def signPostHeaders(privateKeyPem: str, nickname: str, domain: str, \ return signatureHeader def createSignedHeader(privateKeyPem: str,nickname: str,domain: str,port: int, \ - path: str,https: bool,withDigest: bool, \ + path: str,httpPrefix: str,withDigest: bool, \ messageBodyJson: {}) -> {}: headerDomain=domain @@ -79,12 +75,12 @@ def createSignedHeader(privateKeyPem: str,nickname: str,domain: str,port: int, \ headers = {'host': headerDomain, 'digest': f'SHA-256={bodyDigest}'} path='/inbox' signatureHeader = signPostHeaders(privateKeyPem, nickname, domain, port, \ - path, https, None) + path, httpPrefix, None) headers['signature'] = signatureHeader headers['Content-type'] = 'application/json' return headers -def verifyPostHeaders(https: bool, publicKeyPem: str, headers: dict, \ +def verifyPostHeaders(httpPrefix: str, publicKeyPem: str, headers: dict, \ path: str, GETmethod: bool, \ messageBodyJsonStr: str) -> bool: """Returns true or false depending on if the key that we plugged in here @@ -100,10 +96,6 @@ def verifyPostHeaders(https: bool, publicKeyPem: str, headers: dict, \ else: method='POST' - prefix='https' - if not https: - prefix='http' - publicKeyPem = RSA.import_key(publicKeyPem) # Build a dictionary of the signature values signatureHeader = headers['signature'] diff --git a/like.py b/like.py index 983ce039..044528d7 100644 --- a/like.py +++ b/like.py @@ -11,7 +11,7 @@ import commentjson from utils import urlPermitted 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,httpPrefix: str,objectUrl: str,saveToFile: bool) -> {}: """Creates a like Typically toUrl will be a followers collection and ccUrl might be a specific person whose post was liked @@ -20,16 +20,12 @@ def like(baseDir: str,federationList: [],nickname: str,domain: str,port: int, \ if not urlPermitted(objectUrl,federationList): return None - prefix='https' - if not https: - prefix='http' - if port!=80 and port!=443: domain=domain+':'+str(port) newLike = { 'type': 'Like', - 'actor': prefix+'://'+domain+'/users/'+nickname, + 'actor': httpPrefix+'://'+domain+'/users/'+nickname, 'object': objectUrl, 'to': [toUrl], 'cc': [] @@ -44,19 +40,15 @@ def like(baseDir: str,federationList: [],nickname: str,domain: str,port: int, \ return newLike def likePost(baseDir: str,federationList: [], \ - nickname: str, domain: str, port: int, https: bool, \n + nickname: str, domain: str, port: int, httpPrefix: str, \n likeNickname: str, likeDomain: str, likePort: int, likeHttps: bool, \n likeStatusNumber: int,saveToFile: bool) -> {}: """Likes a given status post """ - prefix='https' - if not likeHttps: - prefix='http' - likeDomain=likeDomain if likePort!=80 and likePort!=443: likeDomain=likeDomain+':'+str(likePort) - objectUrl = prefix + '://'+likeDomain+'/users/'+likeNickname+'/statuses/'+str(likeStatusNumber) + objectUrl = httpPrefix + '://'+likeDomain+'/users/'+likeNickname+'/statuses/'+str(likeStatusNumber) - return like(baseDir,federationList,nickname,domain,port,toUrl,ccUrl,https,objectUrl,saveToFile) + return like(baseDir,federationList,nickname,domain,port,toUrl,ccUrl,httpPrefix,objectUrl,saveToFile) diff --git a/person.py b/person.py index 024b5ee1..24e95bd9 100644 --- a/person.py +++ b/person.py @@ -22,16 +22,12 @@ def generateRSAKey() -> (str,str): return privateKeyPem,publicKeyPem def createPerson(baseDir: str,nickname: str,domain: str,port: int, \ - https: bool, saveToFile: bool) -> (str,str,{},{}): + httpPrefix: str, saveToFile: bool) -> (str,str,{},{}): """Returns the private key, public key, actor and webfinger endpoint """ - prefix='https' - if not https: - prefix='http' - privateKeyPem,publicKeyPem=generateRSAKey() webfingerEndpoint= \ - createWebfingerEndpoint(nickname,domain,port,https,publicKeyPem) + createWebfingerEndpoint(nickname,domain,port,httpPrefix,publicKeyPem) if saveToFile: storeWebfingerEndpoint(nickname,domain,baseDir,webfingerEndpoint) @@ -54,29 +50,29 @@ def createPerson(baseDir: str,nickname: str,domain: str,port: int, \ 'toot': 'http://joinmastodon.org/ns#', 'value': 'schema:value'}], 'attachment': [], - 'endpoints': {'sharedInbox': prefix+'://'+domain+'/inbox'}, - 'featured': prefix+'://'+domain+'/users/'+nickname+'/collections/featured', - 'followers': prefix+'://'+domain+'/users/'+nickname+'/followers', - 'following': prefix+'://'+domain+'/users/'+nickname+'/following', + 'endpoints': {'sharedInbox': httpPrefix+'://'+domain+'/inbox'}, + 'featured': httpPrefix+'://'+domain+'/users/'+nickname+'/collections/featured', + 'followers': httpPrefix+'://'+domain+'/users/'+nickname+'/followers', + 'following': httpPrefix+'://'+domain+'/users/'+nickname+'/following', 'icon': {'mediaType': 'image/png', 'type': 'Image', - 'url': prefix+'://'+domain+'/users/'+nickname+'_icon.png'}, - 'id': prefix+'://'+domain+'/users/'+nickname, + 'url': httpPrefix+'://'+domain+'/users/'+nickname+'_icon.png'}, + 'id': httpPrefix+'://'+domain+'/users/'+nickname, 'image': {'mediaType': 'image/png', 'type': 'Image', - 'url': prefix+'://'+domain+'/users/'+nickname+'.png'}, - 'inbox': prefix+'://'+domain+'/users/'+nickname+'/inbox', + 'url': httpPrefix+'://'+domain+'/users/'+nickname+'.png'}, + 'inbox': httpPrefix+'://'+domain+'/users/'+nickname+'/inbox', 'manuallyApprovesFollowers': False, 'name': nickname, - 'outbox': prefix+'://'+domain+'/users/'+nickname+'/outbox', + 'outbox': httpPrefix+'://'+domain+'/users/'+nickname+'/outbox', 'preferredUsername': ''+nickname, - 'publicKey': {'id': prefix+'://'+domain+'/users/'+nickname+'/main-key', - 'owner': prefix+'://'+domain+'/users/'+nickname, + 'publicKey': {'id': httpPrefix+'://'+domain+'/users/'+nickname+'/main-key', + 'owner': httpPrefix+'://'+domain+'/users/'+nickname, 'publicKeyPem': publicKeyPem, 'summary': '', 'tag': [], 'type': 'Person', - 'url': prefix+'://'+domain+'/@'+nickname} + 'url': httpPrefix+'://'+domain+'/@'+nickname} } if saveToFile: @@ -170,7 +166,7 @@ def personLookup(domain: str,path: str,baseDir: str) -> {}: return personJson def personOutboxJson(baseDir: str,domain: str,port: int,path: str, \ - https: bool,noOfItems: int) -> []: + httpPrefix: str,noOfItems: int) -> []: """Obtain the outbox feed for the given person """ if not '/outbox' in path: @@ -204,7 +200,7 @@ def personOutboxJson(baseDir: str,domain: str,port: int,path: str, \ return None if not validNickname(nickname): return None - return createOutbox(baseDir,nickname,domain,port,https, \ + return createOutbox(baseDir,nickname,domain,port,httpPrefix, \ noOfItems,headerOnly,pageNumber) def setPreferredNickname(baseDir: str,nickname: str, domain: str, \ diff --git a/posts.py b/posts.py index 37581cc4..43759a47 100644 --- a/posts.py +++ b/posts.py @@ -250,15 +250,11 @@ def deleteAllPosts(baseDir: str,nickname: str, domain: str) -> None: print(e) def createPostBase(baseDir: str,nickname: str, domain: str, port: int, \ - toUrl: str, ccUrl: str, https: bool, content: str, \ + toUrl: str, ccUrl: str, httpPrefix: str, content: str, \ followersOnly: bool, saveToFile: bool, clientToServer: bool, \ inReplyTo=None, inReplyToAtomUri=None, subject=None) -> {}: """Creates a message """ - prefix='https' - if not https: - prefix='http' - if port!=80 and port!=443: domain=domain+':'+str(port) @@ -266,11 +262,11 @@ def createPostBase(baseDir: str,nickname: str, domain: str, port: int, \ conversationDate=published.split('T')[0] conversationId=statusNumber postTo='https://www.w3.org/ns/activitystreams#Public' - postCC=prefix+'://'+domain+'/users/'+nickname+'/followers' + postCC=httpPrefix+'://'+domain+'/users/'+nickname+'/followers' if followersOnly: postTo=postCC postCC='' - newPostId=prefix+'://'+domain+'/users/'+nickname+'/statuses/'+statusNumber + newPostId=httpPrefix+'://'+domain+'/users/'+nickname+'/statuses/'+statusNumber sensitive=False summary=None if subject: @@ -280,7 +276,7 @@ def createPostBase(baseDir: str,nickname: str, domain: str, port: int, \ newPost = { 'id': newPostId+'/activity', 'type': 'Create', - 'actor': prefix+'://'+domain+'/users/'+nickname, + 'actor': httpPrefix+'://'+domain+'/users/'+nickname, 'published': published, 'to': [toUrl], 'cc': [], @@ -290,12 +286,12 @@ def createPostBase(baseDir: str,nickname: str, domain: str, port: int, \ 'summary': summary, 'inReplyTo': inReplyTo, 'published': published, - 'url': prefix+'://'+domain+'/@'+nickname+'/'+statusNumber, - 'attributedTo': prefix+'://'+domain+'/users/'+nickname, + 'url': httpPrefix+'://'+domain+'/@'+nickname+'/'+statusNumber, + 'attributedTo': httpPrefix+'://'+domain+'/users/'+nickname, 'to': [toUrl], 'cc': [], 'sensitive': sensitive, - 'atomUri': prefix+'://'+domain+'/users/'+nickname+'/statuses/'+statusNumber, + 'atomUri': httpPrefix+'://'+domain+'/users/'+nickname+'/statuses/'+statusNumber, 'inReplyToAtomUri': inReplyToAtomUri, 'conversation': 'tag:'+domain+','+conversationDate+':objectId='+conversationId+':objectType=Conversation', 'content': content, @@ -322,12 +318,12 @@ def createPostBase(baseDir: str,nickname: str, domain: str, port: int, \ 'summary': summary, 'inReplyTo': inReplyTo, 'published': published, - 'url': prefix+'://'+domain+'/@'+nickname+'/'+statusNumber, - 'attributedTo': prefix+'://'+domain+'/users/'+nickname, + 'url': httpPrefix+'://'+domain+'/@'+nickname+'/'+statusNumber, + 'attributedTo': httpPrefix+'://'+domain+'/users/'+nickname, 'to': [toUrl], 'cc': [], 'sensitive': sensitive, - 'atomUri': prefix+'://'+domain+'/users/'+nickname+'/statuses/'+statusNumber, + 'atomUri': httpPrefix+'://'+domain+'/users/'+nickname+'/statuses/'+statusNumber, 'inReplyToAtomUri': inReplyToAtomUri, 'conversation': 'tag:'+domain+','+conversationDate+':objectId='+conversationId+':objectType=Conversation', 'content': content, @@ -352,19 +348,16 @@ def createPostBase(baseDir: str,nickname: str, domain: str, port: int, \ return newPost def createPublicPost(baseDir: str, - nickname: str, domain: str, port: int,https: bool, \ + nickname: str, domain: str, port: int,httpPrefix: str, \ content: str, followersOnly: bool, saveToFile: bool, clientToServer: bool, \ inReplyTo=None, inReplyToAtomUri=None, subject=None) -> {}: """Public post to the outbox """ - prefix='https' - if not https: - prefix='http' return createPostBase(baseDir,nickname, domain, port, \ 'https://www.w3.org/ns/activitystreams#Public', \ - prefix+'://'+domain+'/users/'+nickname+'/followers', \ - https, content, followersOnly, saveToFile, clientToServer, \ + httpPrefix+'://'+domain+'/users/'+nickname+'/followers', \ + httpPrefix, content, followersOnly, saveToFile, clientToServer, \ inReplyTo, inReplyToAtomUri, subject) def threadSendPost(session,postJsonObject: {},federationList: [],inboxUrl: str, \ @@ -394,25 +387,21 @@ def threadSendPost(session,postJsonObject: {},federationList: [],inboxUrl: str, def sendPost(session,baseDir: str,nickname: str, domain: str, port: int, \ toNickname: str, toDomain: str, toPort: int, cc: str, \ - https: bool, content: str, followersOnly: bool, \ + httpPrefix: str, content: str, followersOnly: bool, \ saveToFile: bool, clientToServer: bool, federationList: [], \ sendThreads: [], postLog: [], cachedWebfingers: {},personCache: {}, \ inReplyTo=None, inReplyToAtomUri=None, subject=None) -> int: """Post to another inbox """ - prefix='https' - if not https: - prefix='http' - withDigest=True if toPort!=80 and toPort!=443: toDomain=toDomain+':'+str(toPort) - handle=prefix+'://'+toDomain+'/@'+toNickname + handle=httpPrefix+'://'+toDomain+'/@'+toNickname # lookup the inbox for the To handle - wfRequest = webfingerHandle(session,handle,https,cachedWebfingers) + wfRequest = webfingerHandle(session,handle,httpPrefix,cachedWebfingers) if not wfRequest: return 1 @@ -428,7 +417,7 @@ def sendPost(session,baseDir: str,nickname: str, domain: str, port: int, \ postJsonObject = \ createPostBase(baseDir,nickname,domain,port, \ - toPersonId,cc,https,content, \ + toPersonId,cc,httpPrefix,content, \ followersOnly,saveToFile,clientToServer, \ inReplyTo,inReplyToAtomUri, \ subject) @@ -446,7 +435,7 @@ def sendPost(session,baseDir: str,nickname: str, domain: str, port: int, \ # construct the http header signatureHeaderJson = \ createSignedHeader(privateKeyPem, nickname, domain, port, \ - postPath, https, withDigest, postJsonObject) + postPath, httpPrefix, withDigest, postJsonObject) # Keep the number of threads being used small while len(sendThreads)>10: @@ -462,14 +451,10 @@ def sendPost(session,baseDir: str,nickname: str, domain: str, port: int, \ thr.start() return 0 -def createOutbox(baseDir: str,nickname: str,domain: str,port: int,https: bool, \ +def createOutbox(baseDir: str,nickname: str,domain: str,port: int,httpPrefix: str, \ itemsPerPage: int,headerOnly: bool,pageNumber=None) -> {}: """Constructs the outbox feed """ - prefix='https' - if not https: - prefix='http' - outboxDir = createOutboxDir(nickname,domain,baseDir) if port!=80 and port!=443: @@ -482,16 +467,16 @@ def createOutbox(baseDir: str,nickname: str,domain: str,port: int,https: bool, \ except: pass outboxHeader = {'@context': 'https://www.w3.org/ns/activitystreams', - 'first': prefix+'://'+domain+'/users/'+nickname+'/outbox?page=true', - 'id': prefix+'://'+domain+'/users/'+nickname+'/outbox', - 'last': prefix+'://'+domain+'/users/'+nickname+'/outbox?page=true', + 'first': httpPrefix+'://'+domain+'/users/'+nickname+'/outbox?page=true', + 'id': httpPrefix+'://'+domain+'/users/'+nickname+'/outbox', + 'last': httpPrefix+'://'+domain+'/users/'+nickname+'/outbox?page=true', 'totalItems': 0, 'type': 'OrderedCollection'} outboxItems = {'@context': 'https://www.w3.org/ns/activitystreams', - 'id': prefix+'://'+domain+'/users/'+nickname+'/outbox'+pageStr, + 'id': httpPrefix+'://'+domain+'/users/'+nickname+'/outbox'+pageStr, 'orderedItems': [ ], - 'partOf': prefix+'://'+domain+'/users/'+nickname+'/outbox', + 'partOf': httpPrefix+'://'+domain+'/users/'+nickname+'/outbox', 'type': 'OrderedCollectionPage'} # counter for posts loop @@ -513,7 +498,7 @@ def createOutbox(baseDir: str,nickname: str,domain: str,port: int,https: bool, \ if lastPage<1: lastPage=1 outboxHeader['last']= \ - prefix+'://'+domain+'/users/'+nickname+'/outbox?page='+str(lastPage) + httpPrefix+'://'+domain+'/users/'+nickname+'/outbox?page='+str(lastPage) # Insert posts currPage=1 @@ -524,7 +509,7 @@ def createOutbox(baseDir: str,nickname: str,domain: str,port: int,https: bool, \ # update the prev entry for the last message id postId = prevPostFilename.split('#statuses#')[1].replace('#activity','') outboxHeader['prev']= \ - prefix+'://'+domain+'/users/'+nickname+'/outbox?min_id='+postId+'&page=true' + httpPrefix+'://'+domain+'/users/'+nickname+'/outbox?min_id='+postId+'&page=true' # get the full path of the post file filePath = os.path.join(outboxDir, postFilename) try: @@ -542,7 +527,7 @@ def createOutbox(baseDir: str,nickname: str,domain: str,port: int,https: bool, \ if '/statuses/' in p['id']: postId = p['id'].split('/statuses/')[1].replace('/activity','') outboxHeader['next']= \ - prefix+'://'+domain+'/users/'+ \ + httpPrefix+'://'+domain+'/users/'+ \ nickname+'/outbox?max_id='+ \ postId+'&page=true' postsOnPageCtr += 1 diff --git a/tests.py b/tests.py index 69787962..c5e46d63 100644 --- a/tests.py +++ b/tests.py @@ -43,10 +43,10 @@ def testHttpsigBase(withDigest): print('testHttpsig(' + str(withDigest) + ')') nickname='socrates' domain='argumentative.social' - https=True + httpPrefix='https' port=5576 baseDir=os.getcwd() - privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(baseDir,nickname,domain,port,https,False) + privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(baseDir,nickname,domain,port,httpPrefix,False) messageBodyJsonStr = '{"a key": "a value", "another key": "A string"}' headersDomain=domain @@ -60,11 +60,11 @@ def testHttpsigBase(withDigest): headers = {'host': headersDomain, 'digest': f'SHA-256={bodyDigest}'} path='/inbox' - signatureHeader = signPostHeaders(privateKeyPem, nickname, domain, port, path, https, None) + signatureHeader = signPostHeaders(privateKeyPem, nickname, domain, port, path, httpPrefix, None) headers['signature'] = signatureHeader - assert verifyPostHeaders(https, publicKeyPem, headers, '/inbox' ,False, messageBodyJsonStr) - assert verifyPostHeaders(https, publicKeyPem, headers, '/parambulator/inbox', False , messageBodyJsonStr) == False - assert verifyPostHeaders(https, publicKeyPem, headers, '/inbox', True, messageBodyJsonStr) == False + assert verifyPostHeaders(httpPrefix, publicKeyPem, headers, '/inbox' ,False, messageBodyJsonStr) + assert verifyPostHeaders(httpPrefix, publicKeyPem, headers, '/parambulator/inbox', False , messageBodyJsonStr) == False + assert verifyPostHeaders(httpPrefix, publicKeyPem, headers, '/inbox', True, messageBodyJsonStr) == False if not withDigest: # fake domain headers = {'host': 'bogon.domain'} @@ -74,7 +74,7 @@ def testHttpsigBase(withDigest): bodyDigest = base64.b64encode(SHA256.new(messageBodyJsonStr.encode()).digest()) headers = {'host': domain, 'digest': f'SHA-256={bodyDigest}'} headers['signature'] = signatureHeader - assert verifyPostHeaders(https, publicKeyPem, headers, '/inbox', True, messageBodyJsonStr) == False + assert verifyPostHeaders(httpPrefix, publicKeyPem, headers, '/inbox', True, messageBodyJsonStr) == False def testHttpsig(): testHttpsigBase(False) @@ -111,20 +111,20 @@ def createServerAlice(path: str,domain: str,port: int,federationList: []): os.mkdir(path) os.chdir(path) nickname='alice' - https=False + httpPrefix=False useTor=False clientToServer=False - privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(path,nickname,domain,port,https,True) + privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(path,nickname,domain,port,httpPrefix,True) deleteAllPosts(path,nickname,domain) followPerson(path,nickname,domain,'bob','127.0.0.100:61936',federationList) followerOfPerson(path,nickname,domain,'bob','127.0.0.100:61936',federationList) - createPublicPost(path,nickname, domain, port,https, "No wise fish would go anywhere without a porpoise", False, True, clientToServer) - createPublicPost(path,nickname, domain, port,https, "Curiouser and curiouser!", False, True, clientToServer) - 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, clientToServer) + createPublicPost(path,nickname, domain, port,httpPrefix, "No wise fish would go anywhere without a porpoise", False, True, clientToServer) + createPublicPost(path,nickname, domain, port,httpPrefix, "Curiouser and curiouser!", False, True, clientToServer) + createPublicPost(path,nickname, domain, port,httpPrefix, "In the gardens of memory, in the palace of dreams, that is where you and I shall meet", False, True, clientToServer) global testServerAliceRunning testServerAliceRunning = True print('Server running: Alice') - runDaemon(domain,port,https,federationList,useTor,True) + runDaemon(domain,port,httpPrefix,federationList,useTor,True) def createServerBob(path: str,domain: str,port: int,federationList: []): print('Creating test server: Bob on port '+str(port)) @@ -133,20 +133,20 @@ def createServerBob(path: str,domain: str,port: int,federationList: []): os.mkdir(path) os.chdir(path) nickname='bob' - https=False + httpPrefix='http' useTor=False clientToServer=False - privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(path,nickname,domain,port,https,True) + privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(path,nickname,domain,port,httpPrefix,True) deleteAllPosts(path,nickname,domain) followPerson(path,nickname,domain,'alice','127.0.0.50:61935',federationList) followerOfPerson(path,nickname,domain,'alice','127.0.0.50:61935',federationList) - createPublicPost(path,nickname, domain, port,https, "It's your life, live it your way.", False, True, clientToServer) - createPublicPost(path,nickname, domain, port,https, "One of the things I've realised is that I am very simple", False, True, clientToServer) - createPublicPost(path,nickname, domain, port,https, "Quantum physics is a bit of a passion of mine", False, True, clientToServer) + createPublicPost(path,nickname, domain, port,httpPrefix, "It's your life, live it your way.", False, True, clientToServer) + createPublicPost(path,nickname, domain, port,httpPrefix, "One of the things I've realised is that I am very simple", False, True, clientToServer) + createPublicPost(path,nickname, domain, port,httpPrefix, "Quantum physics is a bit of a passion of mine", False, True, clientToServer) global testServerBobRunning testServerBobRunning = True print('Server running: Bob') - runDaemon(domain,port,https,federationList,useTor,True) + runDaemon(domain,port,httpPrefix,federationList,useTor,True) def testPostMessageBetweenServers(): print('Testing sending message from one server to the inbox of another') @@ -156,7 +156,7 @@ def testPostMessageBetweenServers(): testServerAliceRunning = False testServerBobRunning = False - https=False + httpPrefix='http' useTor=False federationList=['127.0.0.50','127.0.0.100'] @@ -200,7 +200,7 @@ def testPostMessageBetweenServers(): ccUrl=None alicePersonCache={} aliceCachedWebfingers={} - sendResult = sendPost(sessionAlice,aliceDir,'alice', aliceDomain, alicePort, 'bob', bobDomain, bobPort, ccUrl, https, 'Why is a mouse when it spins?', followersOnly, saveToFile, clientToServer, federationList, aliceSendThreads, alicePostLog, aliceCachedWebfingers,alicePersonCache,inReplyTo, inReplyToAtomUri, subject) + sendResult = sendPost(sessionAlice,aliceDir,'alice', aliceDomain, alicePort, 'bob', bobDomain, bobPort, ccUrl, httpPrefix, 'Why is a mouse when it spins?', followersOnly, saveToFile, clientToServer, federationList, aliceSendThreads, alicePostLog, aliceCachedWebfingers,alicePersonCache,inReplyTo, inReplyToAtomUri, subject) print('sendResult: '+str(sendResult)) for i in range(10): @@ -221,14 +221,14 @@ def testFollows(): nickname='test529' domain='testdomain.com' port=80 - https=True + httpPrefix='https' federationList=['wild.com','mesh.com'] baseDir=currDir+'/.tests_testfollows' if os.path.isdir(baseDir): shutil.rmtree(baseDir) os.mkdir(baseDir) os.chdir(baseDir) - createPerson(baseDir,nickname,domain,port,https,True) + createPerson(baseDir,nickname,domain,port,httpPrefix,True) clearFollows(baseDir,nickname,domain) followPerson(baseDir,nickname,domain,'badger','wild.com',federationList) @@ -280,7 +280,7 @@ def testCreatePerson(): nickname='test382' domain='badgerdomain.com' port=80 - https=True + httpPrefix='https' clientToServer=False baseDir=currDir+'/.tests_createperson' if os.path.isdir(baseDir): @@ -288,12 +288,12 @@ def testCreatePerson(): os.mkdir(baseDir) os.chdir(baseDir) - privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(baseDir,nickname,domain,port,https,True) + privateKeyPem,publicKeyPem,person,wfEndpoint=createPerson(baseDir,nickname,domain,port,httpPrefix,True) deleteAllPosts(baseDir,nickname,domain) setPreferredNickname(baseDir,nickname,domain,'badger') setBio(baseDir,nickname,domain,'Randomly roaming in your backyard') archivePosts(nickname,domain,baseDir,4) - createPublicPost(baseDir,nickname, domain, port,https, "G'day world!", False, True, clientToServer, None, None, 'Not suitable for Vogons') + createPublicPost(baseDir,nickname, domain, port,httpPrefix, "G'day world!", False, True, clientToServer, None, None, 'Not suitable for Vogons') os.chdir(currDir) shutil.rmtree(baseDir) diff --git a/webfinger.py b/webfinger.py index 84404825..0d20be27 100644 --- a/webfinger.py +++ b/webfinger.py @@ -22,11 +22,11 @@ def parseHandle(handle: str) -> (str,str): return None, None if '/@' in handle: domain, nickname = \ - handle.replace('https://','').replace('http://','').split('/@') + handle.replace('https://','').replace('http://','').replace('dat://','').split('/@') else: if '/users/' in handle: domain, nickname = \ - handle.replace('https://','').replace('http://','').split('/users/') + handle.replace('https://','').replace('http://','').replace('dat://','').split('/users/') else: if '@' in handle: nickname, domain = handle.split('@') @@ -36,7 +36,7 @@ def parseHandle(handle: str) -> (str,str): return nickname, domain -def webfingerHandle(session,handle: str,https: bool,cachedWebfingers: {}) -> {}: +def webfingerHandle(session,handle: str,httpPrefix: str,cachedWebfingers: {}) -> {}: nickname, domain = parseHandle(handle) if not nickname: return None @@ -47,10 +47,7 @@ def webfingerHandle(session,handle: str,https: bool,cachedWebfingers: {}) -> {}: wf=getWebfingerFromCache(nickname+'@'+wfDomain,cachedWebfingers) if wf: return wf - prefix='https' - if not https: - prefix='http' - url = '{}://{}/.well-known/webfinger'.format(prefix,domain) + url = '{}://{}/.well-known/webfinger'.format(httpPrefix,domain) par = {'resource': 'acct:{}'.format(nickname+'@'+wfDomain)} hdr = {'Accept': 'application/jrd+json'} #try: @@ -83,39 +80,35 @@ def storeWebfingerEndpoint(nickname: str,domain: str,baseDir: str, \ return True def createWebfingerEndpoint(nickname: str,domain: str,port: int, \ - https: bool,publicKeyPem) -> {}: + httpPrefix: str,publicKeyPem) -> {}: """Creates a webfinger endpoint for a user """ - prefix='https' - if not https: - prefix='http' - if port!=80 and port!=443: domain=domain+':'+str(port) account = { "aliases": [ - prefix+"://"+domain+"/@"+nickname, - prefix+"://"+domain+"/users/"+nickname + httpPrefix+"://"+domain+"/@"+nickname, + httpPrefix+"://"+domain+"/users/"+nickname ], "links": [ { - "href": prefix+"://"+domain+"/@"+nickname, + "href": httpPrefix+"://"+domain+"/@"+nickname, "rel": "http://webfinger.net/rel/profile-page", "type": "text/html" }, { - "href": prefix+"://"+domain+"/users/"+nickname+".atom", + "href": httpPrefix+"://"+domain+"/users/"+nickname+".atom", "rel": "http://schemas.google.com/g/2010#updates-from", "type": "application/atom+xml" }, { - "href": prefix+"://"+domain+"/users/"+nickname, + "href": httpPrefix+"://"+domain+"/users/"+nickname, "rel": "self", "type": "application/activity+json" }, { - "href": prefix+"://"+domain+"/api/salmon/1", + "href": httpPrefix+"://"+domain+"/api/salmon/1", "rel": "salmon" }, { @@ -124,7 +117,7 @@ def createWebfingerEndpoint(nickname: str,domain: str,port: int, \ }, { "rel": "http://ostatus.org/schema/1.0/subscribe", - "template": prefix+"://"+domain+"/authorize_interaction?uri={uri}" + "template": httpPrefix+"://"+domain+"/authorize_interaction?uri={uri}" } ], "subject": "acct:"+nickname+"@"+domain