diff options
Diffstat (limited to 'server.py')
-rw-r--r-- | server.py | 87 |
1 files changed, 87 insertions, 0 deletions
diff --git a/server.py b/server.py new file mode 100644 index 0000000..a68efc9 --- /dev/null +++ b/server.py @@ -0,0 +1,87 @@ +from base64 import b64decode +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import padding +from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer +from json import dump, load, loads +from re import search +from requests import get, post +from os.path import isfile +from urllib.parse import quote_plus + + +def collection_append(file, item): + with open(file) as f: + collection = load(f) + collection['totalItems'] += 1 + collection['orderedItems'].append(item) + with open(file, 'w') as f: + dump(collection, f) + + +class fuwuqi(SimpleHTTPRequestHandler): + def do_POST(self): + body = self.rfile.read(int(self.headers['Content-Length'])) + activity = loads(body) + print(activity) + print(self.headers) + print(self.path) + + # Get actor public key + keyid = search('keyId="(.*?)"', self.headers['Signature']).group(1) + actorfile = f'users/{quote_plus(keyid)}' + if not isfile(actorfile): + with open(actorfile, 'w') as f: + f.write(get(keyid).text) + with open(actorfile) as f: + pubkeypem = load(f)['publicKey']['publicKeyPem'].encode('utf8') + pubkey = serialization.load_pem_public_key(pubkeypem, None) + + # Assemble headers + headers = search('headers="(.*?)"', self.headers['Signature']).group(1) + message = '' + for header in headers.split(): + headerval = self.headers[header] + message += f'{header}: {headerval}\n' + + # Verify HTTP signature + signature = search('signature="(.*?)"', self.headers['Signature']).group(1) + pubkey.verify( + b64decode(signature), + message[:-1].encode('utf8'), + padding.PKCS1v15(), + hashes.SHA256() + ) + + # Make sure activity doer matches HTTP signature + actor = keyid.removesuffix('#main-key') + if 'actor' in activity and activity['actor'] != actor: + self.send_response(401) + return + if 'attributedTo' in activity and activity['attributedTo'] != actor: + self.send_response(401) + return + + username = search('^/users/(.*)\.(in|out)box$', self.path).group(1) + if self.path.endswith('inbox'): + # S2S + id = activity['id'].split('/')[-1] + with open(f'users/{username}/{id}', 'w') as f: + dump(activity, f) + elif self.path.endswith('outbox'): + # C2S + collection_append(f'users/{username}.outbox', activity) + + if activity['type'] == 'Create': + id = activity['id'].split('/')[-1] + with open(f'users/{username}.statuses/{id}', 'w') as f: + dump(activity['object'], f) + print(self.headers) + print(body) + resp = post('https://social.exozy.me/inbox', headers=self.headers, data=body) + print(resp) + print(resp.text) + + self.send_response(200) + + +ThreadingHTTPServer(('localhost', 4200), fuwuqi).serve_forever() |