Files
matrix-vona/src/s2s.py

223 lines
6.7 KiB
Python

from flask import Flask, jsonify, Response, request, send_file, abort, Blueprint
from config import *
import globals
import json
import time
import os
server = Blueprint('matrix_server', __name__)
@server.route('/_matrix/federation/v1/version')
def version():
return jsonify({"server": {"version": globals.vona_version,"name": "Vona"}})
@server.route('/_matrix/key/v2/server')
def keys():
return jsonify(globals.sign_json({
"old_verify_keys": {},
"server_name": server_name,
"valid_until_ts": int(time.time() * 1000 + 604800000),
"verify_keys": {
f"ed25519:{signing_key.split()[1]}": {
"key": globals.pubkey()
}
}
}))
@server.route('/_matrix/federation/v1/query/directory')
def room_query():
return jsonify({
"room_id": room_dir_room['chunk'][0]['room_id'],
"servers": [server_name]
})
@server.route('/_matrix/federation/v1/media/download/<media_id>')
def download_media(media_id):
# Auth media requires this to be
# multipart despite not even using
# it for anything. Minor annoyance.
with open(cat, 'rb') as img_file:
image_data = img_file.read()
boundary = '----WebKitFormBoundary7MA4YWxkTrZu0gW'
response_body = (
f'--{boundary}\r\n'
f'Content-Type: application/json\r\n\r\n'
f'{{}}\r\n'
f'--{boundary}\r\n'
f'Content-Type: image/jpeg\r\n'
f'Content-Disposition: attachment; filename="cat.jpg"\r\n\r\n'
).encode() + image_data + f'\r\n--{boundary}--\r\n'.encode()
response = Response(response_body, content_type=f'multipart/mixed; boundary={boundary}')
response.status_code = 200
return response
@server.route('/_matrix/federation/v1/media/thumbnail/<media_id>')
def thumbnail_media(media_id):
return jsonify({"errcode": "M_TOO_CUTE","error": "Cat is too cute to thumbnail"}), 403
@server.route('/_matrix/federation/v1/send_join/<roomId>/<eventId>', methods=['PUT'])
def send_join_v1(roomId, eventId):
abort(500)
@server.route('/_matrix/federation/v2/send_join/<roomId>/<eventId>', methods=['PUT'])
def send_join_v2(roomId, eventId):
abort(500)
@server.route('/_matrix/federation/v1/make_join/<roomId>/<userId>')
def make_join(roomId, userId):
return jsonify({
"event": {
"content": {
"join_authorised_via_users_server": f"@vona:{server_name}",
"membership": "join"
},
"origin": server_name,
"origin_server_ts": str(the_funny_number),
"room_id": roomId,
"sender": userId,
"state_key": userId,
"type": "m.room.member"
},
"room_version": "2"
})
@server.route('/_matrix/federation/v1/publicRooms', methods=['POST', 'GET'])
def room_directory():
return jsonify(room_dir_room)
# https://spec.matrix.org/v1.15/server-server-api/#transactions
@server.route('/_matrix/federation/v1/send/<txnId>', methods=['PUT'])
def federation_send(txnId):
# This only works for v2 rooms, but anything
# bigger than v2 isn't real, so not a problem
# We will need to implement a way to store every
# event we need if we want to send events in the
# future. We don't send events currently, however.
data = request.data.decode('utf-8')
parsed_data = json.loads(data)
response = {"pdus": {}}
if 'pdus' in parsed_data and parsed_data['pdus']:
for pdu in parsed_data['pdus']:
if 'hashes' in pdu and 'sha256' in pdu['hashes']:
pdu_hash = pdu['hashes']['sha256']
response_key = f"${pdu_hash}:{parsed_data['origin']}"
response["pdus"][response_key] = {}
return jsonify(response)
@server.route('/_matrix/federation/v1/query/profile')
def user_profile():
field = request.args.get('field')
if field:
if field == 'avatar_url':
return jsonify({"avatar_url":f"mxc://{server_name}/cat"})
elif field == 'displayname':
return jsonify({"displayname":"Vona"})
else:
return jsonify({"errcode": "M_NOT_FOUND","error": "The requested profile key does not exist."}), 404
return jsonify({"avatar_url": f"mxc://{server_name}/cat","displayname": "Vona"})
@server.route('/_matrix/federation/v1/user/devices/<user>')
def user_devices(user):
return jsonify({
"devices": [],
"stream_id": the_funny_number,
"user_id": f"@vona:{server_name}"
})
@server.route('/_matrix/federation/v1/user/keys/query', methods=['POST'])
def user_keys():
return jsonify({"device_keys":{f"@vona:{server_name}":{}}})
# https://spec.matrix.org/v1.15/server-server-api/#inviting-to-a-room
@server.route('/_matrix/federation/v2/invite/<room>/<txnId>', methods=['PUT'])
def invite_user_v2(room, txnId):
return invite_user(request.data.decode('utf-8'), 2)
@server.route('/_matrix/federation/v1/invite/<room>/<txnId>', methods=['PUT'])
def invite_user_v1(room, txnId):
return invite_user(request.data.decode('utf-8'), 1)
def invite_user(data, endpVer):
try:
invite_data = json.loads(data)
except:
return jsonify({"errcode":"M_NOT_JSON","error":"Content not JSON."}), 400
if "event" in invite_data:
if "room_version" in invite_data:
if invite_data["room_version"] != "2":
return jsonify({
"errcode": "M_INCOMPATIBLE_ROOM_VERSION",
"error": "Vona only supports room version 2.",
"room_version": invite_data["room_version"]
}), 400
else:
if "content" in invite_data["event"]:
if "membership" in invite_data["event"]["content"] and invite_data["event"]["content"]["membership"] == "invite":
if endpVer == 1:
return jsonify([
200,
{"event": globals.sign_json(invite_data["event"])}
])
else:
return jsonify({"event": globals.sign_json(invite_data["event"]), "room_version": "2"})
return jsonify({
"errcode": "M_FORBIDDEN",
"error": "Invalid invitation PDU"
}), 403
@server.route('/_matrix/federation/v1/hierarchy/<roomId>')
def space_hierachy(roomId):
room = room_dir_room['chunk'][0]
return jsonify({
"children": [{
"avatar_url": room['avatar_url'],
"children_state": [{
"content": {"via": [server_name]},
"origin_server_ts": the_funny_number,
"sender": f"@vona:{server_name}",
"state_key": room['room_id'],
"type": "m.space.child"
}],
"guest_can_join": room['guest_can_join'],
"join_rule": room['join_rule'],
"name": room['name'],
"num_joined_members": room['num_joined_members'],
"room_id": room['room_id'],
"room_type": room['room_type'],
"topic": room['topic'],
"world_readable": room['world_readable']
}],
"inaccessible_children": [],
"room": {
"allowed_room_ids": [],
"avatar_url": room['avatar_url'],
"children_state": [{
"content": {"via": [server_name]},
"origin_server_ts": the_funny_number,
"sender": f"@vona:{server_name}",
"state_key": room['room_id'],
"type": "m.space.child"
}],
"guest_can_join": room['guest_can_join'],
"join_rule": room['join_rule'],
"name": room['name'],
"num_joined_members": room['num_joined_members'],
"room_id": room['room_id'],
"room_type": room['room_type'],
"topic": room['topic'],
"world_readable": room['world_readable']
}
})