aboutsummaryrefslogtreecommitdiff
path: root/server.py
blob: dc8f1c8f9d80d011afa4099d3ca86cc408a0996e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
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/users/a/inbox', headers=self.headers, data=body)
            print(resp)
            print(resp.text)
        
        self.send_response(200)


ThreadingHTTPServer(('localhost', 4200), fuwuqi).serve_forever()