This commit is contained in:
2025-09-29 00:51:15 -04:00
parent d3498ba636
commit cef92c3b14
9 changed files with 785 additions and 481 deletions

18
TODO.md
View File

@@ -1,18 +0,0 @@
- add the entirety of Synapse Admin (higher priority to lower priority)
- [X] [Users](https://element-hq.github.io/synapse/latest/admin_api/user_admin_api.html)
- [X] [Server Version](https://element-hq.github.io/synapse/latest/admin_api/version_api.html)
- [X] [Manipulate room membership](https://element-hq.github.io/synapse/latest/admin_api/room_membership.html)
- [X] [Account validity](https://element-hq.github.io/synapse/latest/admin_api/account_validity.html)
- [X] [Server notices](https://element-hq.github.io/synapse/latest/admin_api/server_notices.html)
- [X] [Experimental features](https://element-hq.github.io/synapse/latest/admin_api/experimental_features.html)
- [X] [Register users](https://element-hq.github.io/synapse/latest/admin_api/register_api.html)
- [X] [Purge history](https://element-hq.github.io/synapse/latest/admin_api/purge_history_api.html)
- [X] [Media](https://element-hq.github.io/synapse/latest/admin_api/media_admin_api.html)
- [X] [Statistics](https://element-hq.github.io/synapse/latest/admin_api/statistics.html)
- [X] [Background updates](https://element-hq.github.io/synapse/latest/usage/administration/admin_api/background_updates.html)
- [X] [Reported events](https://element-hq.github.io/synapse/latest/admin_api/event_reports.html)
- [ ] [Federation](https://element-hq.github.io/synapse/latest/usage/administration/admin_api/federation.html)
- [ ] [Registration tokens](https://element-hq.github.io/synapse/latest/usage/administration/admin_api/registration_tokens.html)
- [ ] [Rooms](https://element-hq.github.io/synapse/latest/admin_api/rooms.html)
- implement `/_matrix/client/v1/auth_metadata`

View File

@@ -2,7 +2,7 @@ from flask import Blueprint, jsonify
from config import the_funny_number from config import the_funny_number
import asyncio import asyncio
apps = Blueprint("matrix_appservice", __name__) apps = Blueprint("appservice", __name__)
# This implements both being a homeserver and # This implements both being a homeserver and
# being an appservice. Why? Maximum carnage. # being an appservice. Why? Maximum carnage.
@@ -10,59 +10,26 @@ apps = Blueprint("matrix_appservice", __name__)
# Endpoints invoked by the homeserver are put # Endpoints invoked by the homeserver are put
# lower than ones invoked by the appservice. # lower than ones invoked by the appservice.
# https://spec.matrix.org/v1.16/application-service-api/#pinging
# https://spec.matrix.org/v1.16/application-service-api/#pinging-1
@apps.route("/_matrix/client/v1/appservice/<app>/ping", methods=["POST"]) @apps.route("/_matrix/client/v1/appservice/<app>/ping", methods=["POST"])
async def homeserver_ping(app): async def homeserver_ping(app):
# Sleeping here makes it more realistic # Sleeping here makes it more realistic
await asyncio.sleep(the_funny_number / 1000) await asyncio.sleep(the_funny_number / 1000)
return jsonify({"duration_ms": the_funny_number}) return jsonify({"duration_ms": the_funny_number})
@apps.route("/_matrix/client/v3/directory/list/appservice/<net>/<room>", methods=["PUT"])
@apps.route("/_matrix/app/v1/ping", methods=["POST"]) @apps.route("/_matrix/app/v1/ping", methods=["POST"])
async def app_ping(): @apps.route("/_matrix/app/v1/transactions/<txnId>", methods=["PUT"])
@apps.route("/_matrix/app/v1/thirdparty/protocol/<protocol>")
@apps.route("/_matrix/app/v1/rooms/<room>")
@apps.route("/_matrix/app/v1/users/<user>")
async def empty_dict(**kwargs):
return jsonify({}) return jsonify({})
# https://spec.matrix.org/v1.16/application-service-api/#querying @apps.route("/_matrix/app/v1/thirdparty/location")
@apps.route('/_matrix/app/v1/users/<user>') @apps.route("/_matrix/app/v1/thirdparty/location/<protocol>")
async def query_user(user): @apps.route("/_matrix/app/v1/thirdparty/user")
return jsonify({}) @apps.route("/_matrix/app/v1/thirdparty/user/<protocol>")
async def empty_array(**kwargs):
@apps.route('/_matrix/app/v1/rooms/<room>')
async def query_room(room):
return jsonify({})
# https://spec.matrix.org/v1.16/application-service-api/#pushing-events
@apps.route('/_matrix/app/v1/transactions/<txnId>', methods=['PUT'])
async def send_transaction(txnId):
return jsonify({})
# https://spec.matrix.org/v1.16/application-service-api/#third-party-networks
@apps.route('/_matrix/app/v1/thirdparty/location')
async def location():
return jsonify([]) return jsonify([])
@apps.route('/_matrix/app/v1/thirdparty/location/<protocol>')
async def location_from_protocol(protocol):
return jsonify([])
@apps.route('/_matrix/app/v1/thirdparty/protocol/<protocol>')
async def protocol_info(protocol):
return jsonify({})
@apps.route('/_matrix/app/v1/thirdparty/user')
async def thirdparty_user():
return jsonify([])
@apps.route('/_matrix/app/v1/thirdparty/user/<protocol>')
async def user_from_protocol(protocol):
return jsonify([])
# https://spec.matrix.org/v1.16/application-service-api/#published-room-directories
@apps.route('/_matrix/client/v3/directory/list/appservice/<net>/<room>', methods=['PUT'])
async def publish_room(net, room):
return jsonify({})

View File

@@ -6,27 +6,100 @@ import asyncio
import random import random
import os import os
client = Blueprint('matrix_client', __name__) client = Blueprint("c2s", __name__)
@client.route("/_matrix/client/v3/account/password", methods=["POST"])
@client.route('/_matrix/client/v3/user/<user>/account_data/<type>', methods=['GET', 'PUT'])
@client.route('/_matrix/client/r0/user/<user>/account_data/<type>', methods=['GET', 'PUT'])
@client.route('/_matrix/client/v3/sendToDevice/<event>/<txnId>', methods=["PUT"])
@client.route('/_matrix/media/v3/upload/<server>/<media>', methods=["PUT"])
@client.route('/_matrix/client/v3/thirdparty/protocols')
@client.route('/_matrix/client/r0/thirdparty/protocols')
@client.route('/_matrix/client/v3/delete_devices', methods=['POST'])
@client.route('/_matrix/client/r0/delete_devices', methods=['POST'])
@client.route('/_matrix/client/v3/logout/all', methods=['POST'])
@client.route('/_matrix/client/v3/logout', methods=['POST'])
@client.route('/_matrix/client/v3/rooms/<room>/invite', methods=['POST'])
@client.route('/_matrix/client/v3/rooms/<roomId>/leave', methods=['POST'])
@client.route('/_matrix/client/r0/rooms/<roomId>/leave', methods=['POST'])
@client.route('/_matrix/client/v3/rooms/<roomId>/read_markers', methods=['POST'])
@client.route('/_matrix/client/r0/rooms/<roomId>/read_markers', methods=['POST'])
@client.route('/_matrix/client/v3/keys/device_signing/upload', methods=['POST'])
@client.route("/_matrix/client/v3/rooms/<room>/receipt/<type>/<event>", methods=['POST'])
@client.route("/_matrix/client/v3/users/<user>/report", methods=["POST"])
@client.route('/_matrix/client/v3/voip/turnServer')
@client.route('/_matrix/client/r0/voip/turnServer')
@client.route("/_matrix/client/v3/rooms/<r>/report/<e>")
@client.route("/_matrix/client/v3/rooms/<r>/report")
@client.route("/_matrix/client/v3/users/<u>/report")
async def empty_response(**kwargs):
return jsonify({})
@client.route('/_matrix/client/versions') @client.route('/_matrix/client/versions')
def client_version(): async def spec_versions():
return jsonify({ return jsonify({
"versions": [ "versions": (
"r0.6.0", ["r0.0.0"] + [f"r0.{i}.0" for i in range(1, 7)] +
"v1.16" ["r0.6.1"] + [f"v1.{i}" for i in range(1, 17)]
], ),
"unstable_features": {"uk.tcpip.msc4133": True} "unstable_features": {
"uk.half-shot.msc2666": True,
"uk.timedout.msc4323": True
}
}) })
@client.route('/_matrix/client/v3/admin/whois/<userId>')
def admin_whois(userId):
if userId.startswith('@'):
return jsonify({"errcode":"M_MISSING_TOKEN","error":"Missing access token"})
return jsonify({"errcode":"M_INVALID_PARAM","error":"Expected UserID string to start with '@'"}) @client.route("/_matrix/client/v3/admin/whois/<user>")
@client.route("/_matrix/client/r0/admin/whois/<user>")
async def whois(user):
if userId.startswith("@"):
return jsonify({
"devices": {
"": {
"sessions": [{
"connections": [{
"ip": "127.0.0.1",
"last_seen": the_funny_number,
"user_agent": f"Vona/{globals.vona_version}"
}]
}]
}
},
"user_id": user
})
return jsonify({
"errcode": "M_INVALID_PARAM",
"error": "Expected UserID string to start with '@'"
})
@client.route("/_matrix/client/unstable/uk.timedout.msc4323/admin/suspend/<user>", methods=["GET", "PUT"])
@client.route("/_matrix/client/v1/admin/suspend/<user>", methods=["GET", "PUT"])
async def suspend(user):
if request.method == "PUT":
req = request.get_json()
if req and "suspended" in req:
return jsonify({"suspended": req["suspended"]})
return jsonify({"suspended": True})
@client.route("/_matrix/client/unstable/uk.timedout.msc4323/admin/lock/<user>", methods=["GET", "PUT"])
@client.route("/_matrix/client/v1/admin/lock/<user>", methods=["GET", "PUT"])
async def lock(user):
if request.method == "PUT":
req = request.get_json()
if req and "locked" in req:
return jsonify({"locked": req["locked"]})
return jsonify({"locked": True})
@client.route('/_matrix/client/v3/rooms/<roomId>/members') @client.route('/_matrix/client/v3/rooms/<roomId>/members')
def room_member_count(roomId): @client.route('/_matrix/client/r0/rooms/<roomId>/members')
async def room_member_count(roomId):
return jsonify({ return jsonify({
"chunk": [{ "chunk": [{
"content": { "content": {
@@ -44,15 +117,19 @@ def room_member_count(roomId):
}] }]
}) })
@client.route('/_matrix/client/v3/account/whoami')
def whoami(): @client.route("/_matrix/client/v3/account/whoami")
async def whoami():
return jsonify({ return jsonify({
"device_id": "VVOONNAA", "device_id": "VVOONNAA",
"user_id": f"@vona:{server_name}" "user_id": f"@vona:{server_name}"
}) })
@client.route('/_matrix/client/v3/register', methods=['POST']) @client.route('/_matrix/client/v3/register', methods=['POST'])
def register(): @client.route('/_matrix/client/v1/register', methods=['POST'])
@client.route('/_matrix/client/r0/register', methods=['POST'])
async def register():
if users_can_register: if users_can_register:
try: try:
data = request.get_json() data = request.get_json()
@@ -79,8 +156,10 @@ def register():
"error": "M_FORBIDDEN: Registration has been disabled." "error": "M_FORBIDDEN: Registration has been disabled."
}), 403 }), 403
@client.route('/_matrix/client/r0/login', methods=['GET', 'POST'])
@client.route('/_matrix/client/v3/login', methods=['GET', 'POST']) @client.route('/_matrix/client/v3/login', methods=['GET', 'POST'])
def login(): async def login():
if request.method == 'GET': if request.method == 'GET':
return jsonify({ return jsonify({
"flows": [ "flows": [
@@ -100,15 +179,17 @@ def login():
}) })
@client.route('/_matrix/client/v3/account/password/email/requestToken', methods=['POST']) @client.route('/_matrix/client/v3/account/password/email/requestToken', methods=['POST'])
def pswd_reset(): async def pswd_reset():
return jsonify({"errcode":"M_THREEPID_NOT_FOUND","error":"Email not found"}), 400 return jsonify({"errcode":"M_THREEPID_NOT_FOUND","error":"Email not found"}), 400
@client.route('/_matrix/client/v3/keys/upload', methods=['POST']) @client.route('/_matrix/client/v3/keys/upload', methods=['POST'])
def key_upload(): async def key_upload():
return jsonify({"one_time_key_counts":{"signed_curve25519":50}}) return jsonify({"one_time_key_counts":{"signed_curve25519":50}})
@client.route('/_matrix/client/v3/room_keys/version', methods=['POST', 'GET'])
def roomkeys(): @client.route("/_matrix/client/v3/room_keys/version", methods=['POST', 'GET'])
@client.route("/_matrix/client/unstable/room_keys/version", methods=['POST', 'GET'])
async def room_keys():
if request.method == 'POST': if request.method == 'POST':
return jsonify({"version": str(the_funny_number)}) return jsonify({"version": str(the_funny_number)})
@@ -128,37 +209,82 @@ def roomkeys():
"version": str(the_funny_number) "version": str(the_funny_number)
}) })
@client.route('/_matrix/client/v3/capabilities')
def capabilities():
return jsonify({"capabilities":{"m.room_versions":{"default":"2","available":{"1":"stable","2":"stable","3":"stable","4":"stable","5":"stable","6":"stable","7":"stable","8":"stable","1337":"stable","9":"stable","10":"stable","11":"stable","org.matrix.msc3757.10":"stable","org.matrix.msc3757.11":"stable"},"org.matrix.msc3244.room_capabilities":{"knock":{"preferred":"7","support":["7","8","9","10","11","org.matrix.msc3757.10","org.matrix.msc3757.11"]},"restricted":{"preferred":"9","support":["8","9","10","11","org.matrix.msc3757.10","org.matrix.msc3757.11"]}}},"m.change_password":{"enabled":True},"m.3pid_changes":{"enabled":True},"m.get_login_token":{"enabled":False},"m.profile_fields":{"enabled":True,"allowed":["*"]}}})
@client.route("/_matrix/client/v3/capabilities")
@client.route("/_matrix/client/r0/capabilities")
async def capabilities():
return jsonify({
"capabilities": {
"m.room_versions": {
"default": "2",
"available": {
"1": "stable",
"2": "stable",
"3": "stable",
"4": "stable",
"5": "stable",
"6": "stable",
"7": "stable",
"8": "stable",
"9": "stable",
"10": "stable",
"11": "stable",
"12": "stable"
},
"org.matrix.msc3244.room_capabilities": {
"knock": {
"preferred":"7",
"support": [
"7","8","9","10","11","12"
]
},
"restricted": {
"preferred": "9",
"support": ["8","9","10","11","12"]
}
}
},
"m.change_password": {"enabled": True},
"m.3pid_changes": {"enabled": True},
"m.get_login_token": {"enabled": False},
"m.profile_fields": {
"enabled": True,
"allowed":["*"]
},
"account_moderation": {
"suspend": True,
"lock": True
}
}
})
@client.route('/_matrix/client/r0/pushrules/')
@client.route('/_matrix/client/v3/pushrules/') @client.route('/_matrix/client/v3/pushrules/')
def pushrules(): async def pushrules():
# TODO: Actually implement this # TODO: Actually implement this
return jsonify({"errcode":"M_MISSING_TOKEN","error":"Missing access token"})
@client.route('/_matrix/client/v3/user/<user>/filter', methods=['POST'])
def filter(user):
return jsonify({"filter_id": "vvvooonnnaaa"})
@client.route('/_matrix/client/v3/user/<user>/filter/<data>')
def filter_two(user, data):
return jsonify({"filter_id": "vvvooonnnaaa"})
@client.route('/_matrix/client/v3/rooms/<room>/invite', methods=['POST'])
def invite_room(room):
return jsonify({}) return jsonify({})
@client.route('/_matrix/client/v3/knock/<room>', methods=['POST'])
def knock_room(room): @client.route('/_matrix/client/v3/user/<user>/filter/<data>')
return jsonify({"errcode":"M_FORBIDDEN","error":"You are not allowed to knock on this room."}), 403 @client.route('/_matrix/client/r0/user/<user>/filter/<data>')
@client.route('/_matrix/client/v3/user/<user>/filter', methods=['POST'])
@client.route('/_matrix/client/r0/user/<user>/filter', methods=['POST'])
async def filter(**kwargs):
return jsonify({"filter_id": "vvvooonnnaaa"})
@client.route('/_matrix/client/v3/join/<room>', methods=['POST']) @client.route('/_matrix/client/v3/join/<room>', methods=['POST'])
@client.route('/_matrix/client/r0/join/<room>', methods=['POST'])
@client.route('/_matrix/client/v3/rooms/<room>/join', methods=['POST']) @client.route('/_matrix/client/v3/rooms/<room>/join', methods=['POST'])
def join_roomId(room): @client.route('/_matrix/client/v3/knock/<room>', methods=['POST'])
async def join(room):
return jsonify({"room_id": room}) return jsonify({"room_id": room})
@client.route('/_matrix/client/v3/sync')
@client.route("/_matrix/client/v3/initialSync")
@client.route("/_matrix/client/v3/sync")
@client.route("/_matrix/client/r0/sync")
async def sync(): async def sync():
class bullshit: class bullshit:
def get_json(): def get_json():
@@ -183,7 +309,10 @@ async def sync():
return new_list return new_list
room = globals.make_event_id().replace("$", "!") room = globals.make_event_id().replace("$", "!")
old_room_state = send_join(bullshit, room)["state"] old_room_state = send_join(
bullshit,
room
)["state"]
room_state = await remove_keys( room_state = await remove_keys(
old_room_state, old_room_state,
@@ -245,45 +374,64 @@ async def sync():
} }
}) })
@client.route('/_matrix/client/v3/rooms/<room>/send/<eventType>/<txnId>', methods=['POST', 'PUT']) @client.route('/_matrix/client/v3/rooms/<room>/send/<eventType>/<txnId>', methods=['POST', 'PUT'])
def send_message(room, eventType, txnId): @client.route('/_matrix/client/r0/rooms/<room>/send/<eventType>/<txnId>', methods=['POST', 'PUT'])
async def send_message(room, eventType, txnId):
return jsonify({"event_id": globals.make_event_id()}), 200 return jsonify({"event_id": globals.make_event_id()}), 200
@client.route('/_matrix/client/v3/user_directory/search', methods=['POST']) @client.route('/_matrix/client/v3/user_directory/search', methods=['POST'])
def user_directory(): async def user_directory():
return jsonify({"limited":False,"results":[{"avatar_url":f"mxc://{server_name}/cat","display_name":"Vona","user_id":f"@vona:{server_name}"}]}) return jsonify({
"limited": False,
"results": [{
"avatar_url": f"mxc://{server_name}/cat",
"display_name": "Vona",
"user_id": f"@vona:{server_name}"
}]
})
@client.route('/_matrix/client/v3/devices') @client.route('/_matrix/client/v3/devices')
def devices(): @client.route('/_matrix/client/r0/devices')
return jsonify({"devices":[{"device_id":"VVOONNAA","display_name":"Vona","last_seen_ip":"127.0.0.1","last_seen_ts":the_funny_number}]}) async def devices():
@client.route('/_matrix/client/v3/devices/<deviceId>', methods=['GET', 'PUT', 'DELETE'])
def get_device():
if request.method == 'GET':
return jsonify({"device_id": "VVOONNAA","display_name": "Vona","last_seen_ip": "127.0.0.1","last_seen_ts": the_funny_number})
return jsonify({})
@client.route('/_matrix/client/v3/delete_devices', methods=['POST'])
@client.route('/_matrix/client/v3/logout/all', methods=['POST'])
@client.route('/_matrix/client/v3/logout', methods=['POST'])
def delete_devices():
return jsonify({})
@client.route('/_matrix/client/v3/refresh', methods=['POST'])
def refresh():
return jsonify({"errcode": "M_UNKNOWN_TOKEN","error": "Soft logged out","soft_logout": True})
@client.route('/_matrix/client/v3/voip/turnServer')
def turnserver():
return jsonify({ return jsonify({
"errcode": "M_LIMIT_EXCEEDED", "devices": [{
"error": "Too many requests", "device_id": "VVOONNAA",
"retry_after_ms": the_funny_number "display_name": "Vona",
}), 429 "last_seen_ip": "127.0.0.1",
"last_seen_ts": the_funny_number
}]
})
@client.route('/_matrix/client/v3/devices/<device>', methods=['GET', 'PUT', 'DELETE'])
@client.route('/_matrix/client/r0/devices/<device>', methods=['GET', 'PUT', 'DELETE'])
async def get_device(device):
if request.method == "GET":
return jsonify({
"device_id": device,
"display_name": "Vona",
"last_seen_ip": "127.0.0.1",
"last_seen_ts": the_funny_number
})
return jsonify({})
@client.route("/_matrix/client/v3/refresh", methods=['POST'])
async def refresh():
return jsonify({
"access_token": "vona",
"expires_in_ms": the_funny_number * 1000,
"refresh_token": "vona"
})
@client.route('/_matrix/client/unstable/im.nheko.summary/rooms/<roomId>/summary') @client.route('/_matrix/client/unstable/im.nheko.summary/rooms/<roomId>/summary')
@client.route('/_matrix/client/unstable/im.nheko.summary/summary/<roomId>') @client.route('/_matrix/client/unstable/im.nheko.summary/summary/<roomId>')
@client.route('/_matrix/client/v1/room_summary/<roomId>')
def unstable_room_summary(roomId): def unstable_room_summary(roomId):
room = room_dir_room['chunk'][0] room = room_dir_room['chunk'][0]
return jsonify({ return jsonify({
@@ -300,59 +448,93 @@ def unstable_room_summary(roomId):
"room_version": 2, "room_version": 2,
}) })
@client.route('/_matrix/client/v1/room_summary/<roomId>')
def room_summary(roomId):
room = room_dir_room['chunk'][0]
return jsonify({
"room_id": room['room_id'],
"avatar_url": room['avatar_url'],
"guest_can_join": room['guest_can_join'],
"name": room['name'],
"num_joined_members": room['num_joined_members'],
"topic": room['topic'],
"world_readable": room['world_readable'],
"join_rule": room['join_rule'],
"room_type": room['room_type'],
"membership": "join",
"room_version": 2,
})
@client.route('/_matrix/client/r0/directory/room/<roomId>') @client.route("/_matrix/client/v3/directory/room/<room>", methods=["GET", "PUT", "DELETE"])
def r0_room_query(roomId): @client.route("/_matrix/client/r0/directory/room/<room>")
async def room_query(room):
if request.method == "GET":
return jsonify({ return jsonify({
"room_id": room_dir_room['chunk'][0]['room_id'], "room_id": room_dir_room['chunk'][0]['room_id'],
"servers": [server_name] "servers": [server_name]
}) })
@client.route('/_matrix/client/r0/directory/list/room/<roomId>') return jsonify({})
def r0_room_vis(roomId):
@client.route("/_matrix/client/v3/rooms/<room>/aliases")
async def room_aliases(room):
return jsonify({
"aliases": [
f"#vona:{server_name}"
]
})
@client.route("/_matrix/client/v3/directory/list/room/<room>", methods=["GET", "PUT"])
@client.route('/_matrix/client/r0/directory/list/room/<room>', methods=["GET", "PUT"])
async def room_visibility(room):
return jsonify({"visibility": "public"}) return jsonify({"visibility": "public"})
@client.route('/_matrix/client/r0/publicRooms') @client.route("/_matrix/client/v3/search", methods=["POST"])
def r0_room_directory(): async def search():
return jsonify(room_dir_room) room = globals.make_event_id().replace("$", "!")
event = globals.make_event_id()
return jsonify({
"search_categories": {
"room_events": {
"count": 1,
"groups": {
"room_id": {
room: {
"next_batch": "vona",
"order": 1,
"results": [event]
}
}
},
"highlights": [],
"next_batch": "vona",
"results": [{
"rank": the_funny_number,
"result": {
"content": {
"msgtype": "m.text",
"body": "Number 15: Burger King Foot Lettuce.\nThe last thing you'd want in your Burger King burger is someones foot fungus, but as it turns out, that might be what you get. A 4channer uploaded a photo, anonymously to the site showcasing his feet in a plastic bin of lettuce with the statement \"This is the lettuce you eat at Burger King.\". Admittedly, he had shoes on, but thats even worse. The post went live at 11:38 PM on July 16 and a mere 20 minutes later the Burger King in question was alerted to the rogue employee. At least, I hope hes rogue. How did it happen? Well, the BK employee hadn't removed the EXIF data from the uploaded photo, which suggested that the culprit was somewhere in Mayfield Heights, Ohio. This was at 11:47. 3 minutes later, at 11:50, the Burger King branch was posted with wishes of happy unemployment. 5 minutes later, the news station was contacted by another 4channer, and 3 minutes later at 11:58 a link was posted: BK's tell us about us online forum. The foot photo, otherwise known as Exhibit A, was attached. Cleveland Seen Magazine contacted the BK in question and the next day when questioned, the breakfast shift manager said \"Oh, I know who that is, hes getting fired\". Mystery solved, by 4chan. Now we can go back to eating our fast food in peace.",
"format": "org.matrix.custom.html",
"formatted_body": "Number 15: Burger King Foot Lettuce.<br />The last thing you'd want in your Burger King burger is someones foot fungus, but as it turns out, that might be what you get. A 4channer uploaded a photo, anonymously to the site showcasing his feet in a plastic bin of lettuce with the statement &quot;This is the lettuce you eat at Burger King.&quot;. Admittedly, he had shoes on, but thats even worse. The post went live at 11:38 PM on July 16 and a mere 20 minutes later the Burger King in question was alerted to the rogue employee. At least, I hope hes rogue. How did it happen? Well, the BK employee hadn't removed the EXIF data from the uploaded photo, which suggested that the culprit was somewhere in Mayfield Heights, Ohio. This was at 11:47. 3 minutes later, at 11:50, the Burger King branch was posted with wishes of happy unemployment. 5 minutes later, the news station was contacted by another 4channer, and 3 minutes later at 11:58 a link was posted: BK's tell us about us online forum. The foot photo, otherwise known as Exhibit A, was attached. Cleveland Seen Magazine contacted the BK in question and the next day when questioned, the breakfast shift manager said &quot;Oh, I know who that is, hes getting fired&quot;. Mystery solved, by 4chan. Now we can go back to eating our fast food in peace."
},
"event_id": event,
"origin_server_ts": the_funny_number,
"room_id": room,
"sender": f"@vona:{server_name}",
"type": "m.room.message"
}
}]
}
}
})
@client.route('/_matrix/media/v1/thumbnail/<server>/<file>') @client.route('/_matrix/media/v1/thumbnail/<server>/<file>')
@client.route('/_matrix/client/v1/media/thumbnail/<s>/<f>') @client.route('/_matrix/client/v1/media/thumbnail/<s>/<f>')
@client.route('/_matrix/media/r0/thumbnail/<server>/<file>') @client.route('/_matrix/media/r0/thumbnail/<server>/<file>')
def media(server, file): @client.route('/_matrix/media/v3/thumbnail/<server>/<media>')
@client.route('/_matrix/media/v3/download/<server>/<media>/<file>')
@client.route('/_matrix/client/v1/media/download/<s>/<f>')
@client.route('/_matrix/media/v3/download/<server>/<media>')
@client.route('/_matrix/media/r0/download/<server>/<media>')
async def media(**kwargs):
return send_file(cat) return send_file(cat)
@client.route('/_matrix/client/r0/admin/whois/<userId>')
def whois(userId):
return jsonify({"user_id":f"@vona:{server_name}","devices":{"":{"sessions":[{"connections":[{"ip":f"{the_funny_number}.{the_funny_number}.{the_funny_number}.{the_funny_number}","last_seen":the_funny_number,"user_agent":f"Vona/{the_funny_number}.0"},{"ip":f"{the_funny_number}.{the_funny_number}.{the_funny_number}.{the_funny_number}","last_seen":the_funny_number,"user_agent":f"Vona/{the_funny_number}.0"}]}]}}})
@client.route('/_matrix/client/v3/register/available') @client.route('/_matrix/client/v3/register/available')
@client.route('/_matrix/client/r0/register/available') @client.route('/_matrix/client/r0/register/available')
def username_available(): async def username_available():
return jsonify({"available": True}) return jsonify({"available": True})
@client.route('/_matrix/client/v3/thirdparty/protocols')
def thirdparty_protocols():
return jsonify({})
@client.route('/_matrix/media/v3/preview_url') @client.route('/_matrix/media/v3/preview_url')
def url_preview(): async def url_preview():
return jsonify({ return jsonify({
"matrix:image:size": 102400, "matrix:image:size": 102400,
"og:description": "look at this cool cat", "og:description": "look at this cool cat",
@@ -364,49 +546,28 @@ def url_preview():
}) })
@client.route('/_matrix/client/v1/media/preview_url') @client.route('/_matrix/client/v1/media/preview_url')
def media_preview(): async def media_preview():
response = send_file(cat) response = send_file(cat)
response.headers['Content-Disposition'] = f'inline; filename="cat.jpg"' response.headers['Content-Disposition'] = f'inline; filename="cat.jpg"'
response.headers['Content-Type'] = 'image/jpg' response.headers['Content-Type'] = 'image/jpg'
return response return response
@client.route('/_matrix/media/v1/create', methods=['POST'])
def create_mxc():
return jsonify({"content_uri": f"mxc://{server_name}/cat"})
@client.route('/_matrix/media/v3/upload', methods=['POST']) @client.route('/_matrix/media/v3/upload', methods=['POST'])
def upload_media(): @client.route('/_matrix/media/r0/upload', methods=['POST'])
@client.route('/_matrix/media/v1/create', methods=['POST'])
async def upload_media():
return jsonify({"content_uri": f"mxc://{server_name}/cat"}) return jsonify({"content_uri": f"mxc://{server_name}/cat"})
@client.route('/_matrix/media/v3/upload/<server>/<media>', methods=['PUT'])
def upload_media_to_mxc(server, media):
return jsonify({})
@client.route('/_matrix/media/v3/config') @client.route('/_matrix/media/v3/config')
def media_config(): async def media_config():
return jsonify({"m.upload.size": the_funny_number}) return jsonify({"m.upload.size": the_funny_number * 69420})
@client.route('/_matrix/media/v3/download/<server>/<media>')
def v3_download_media(server, media):
return send_file(cat)
@client.route('/_matrix/media/v3/download/<server>/<media>/<file>')
def v3_download_file(server, media, file):
return send_file(cat)
@client.route('/_matrix/media/v3/thumbnail/<server>/<media>')
def v3_thumbnail_media(server, media):
return send_file(cat)
@client.route('/_matrix/client/v3/sendToDevice/<event>/<txnId>', methods=['PUT'])
def send_to_device(event, txnId):
return jsonify({})
@client.route('/_matrix/client/v3/profile/<userId>/<key>', methods=['GET', 'PUT', 'DELETE']) @client.route('/_matrix/client/v3/profile/<userId>/<key>', methods=['GET', 'PUT', 'DELETE'])
def user_profile_keys(userId, key): @client.route('/_matrix/client/r0/profile/<userId>/<key>', methods=['GET', 'PUT', 'DELETE'])
if request.method == 'PUT' or request.method == 'DELETE': async def profile_keys(userId, key):
return jsonify({}) if request.method == 'GET':
elif request.method == 'GET':
if key == 'avatar_url': if key == 'avatar_url':
return jsonify({"avatar_url": f"mxc://{server_name}/cat"}) return jsonify({"avatar_url": f"mxc://{server_name}/cat"})
elif key == 'displayname': elif key == 'displayname':
@@ -417,12 +578,20 @@ def user_profile_keys(userId, key):
"error": "The requested profile key does not exist." "error": "The requested profile key does not exist."
}) })
return jsonify({})
@client.route('/_matrix/client/v3/profile/<userId>') @client.route('/_matrix/client/v3/profile/<userId>')
def user_profile(userId): @client.route('/_matrix/client/r0/profile/<userId>')
return jsonify({"avatar_url":f"mxc://{server_name}/cat","displayname":"Vona"}) async def user_profile(userId):
return jsonify({
"avatar_url": f"mxc://{server_name}/cat",
"displayname": "Vona"
})
@client.route('/_matrix/client/v3/rooms/<roomId>/messages') @client.route('/_matrix/client/v3/rooms/<roomId>/messages')
def room_messages(roomId): @client.route('/_matrix/client/r0/rooms/<roomId>/messages')
async def room_messages(roomId):
return jsonify({ return jsonify({
"chunk": [{ "chunk": [{
"content": { "content": {
@@ -441,29 +610,39 @@ def room_messages(roomId):
"start": f"{os.urandom(16).hex()}" "start": f"{os.urandom(16).hex()}"
}) })
@client.route('/_matrix/client/v3/rooms/<roomId>/leave', methods=['POST'])
def leave_room(roomId):
return jsonify({})
@client.route('/_matrix/client/v3/rooms/<roomId>/read_markers', methods=['POST'])
def read_markers(roomId):
return jsonify({})
@client.route('/_matrix/client/v3/user/<user>/account_data/<type>', methods=['PUT'])
async def set_custom_account_data(user, type):
return jsonify({})
@client.route('/_matrix/client/v3/keys/device_signing/upload', methods=['POST'])
async def upload_device_signing_keys():
return jsonify({})
@client.route('/_matrix/client/v3/keys/query', methods=['POST']) @client.route('/_matrix/client/v3/keys/query', methods=['POST'])
@client.route('/_matrix/client/r0/keys/query', methods=['POST'])
async def query_keys(): async def query_keys():
# Should be replaced eventually # Should be replaced eventually
return jsonify({}) return jsonify({})
@client.route("/_matrix/client/api/v1/createRoom", methods=["POST"])
@client.route("/_matrix/client/v3/createRoom", methods=["POST"])
@client.route("/_matrix/client/r0/createRoom", methods=["POST"])
async def create_room():
return jsonify({"room_id": room_dir_room["chunk"][0]["room_id"]})
# https://spec.matrix.org/v1.16/client-server-api/#post_matrixclientv3roomsroomidreceiptreceipttypeeventid
@client.route("/_matrix/client/v3/rooms/<room>/receipt/<type>/<event>", methods=['POST']) @client.route("/_matrix/client/unstable/uk.half-shot.msc2666/mutual_rooms")
def send_read_receipt(room, type, event): @client.route("/_matrix/client/v1/user/mutual_rooms")
async def mutual_rooms():
return jsonify({
"joined": [
room_dir_room["chunk"][0]["room_id"]
]
})
@client.route("/_matrix/client/r0/presence/<user>/status", methods=["GET", "PUT"])
async def presence(user):
if request.method == "PUT":
return jsonify({}) return jsonify({})
return jsonify({
"presence": "online"
})
@client.route('/_matrix/client/r0/publicRooms', methods=["GET", "POST"])
async def room_directory():
return jsonify(room_dir_room)

View File

@@ -5,7 +5,7 @@ import base64
import re import re
import os import os
custom = Blueprint('matrix_custom', __name__) custom = Blueprint("custom", __name__)
# This implements custom endpoints # This implements custom endpoints
# used by other homeserver # used by other homeserver
@@ -16,6 +16,29 @@ custom = Blueprint('matrix_custom', __name__)
# files eventually. # files eventually.
@custom.route('/_synapse/admin/v1/suspend/<user_id>', methods=["PUT"])
@custom.route('/_synapse/admin/v1/deactivate/<user_id>', methods=['POST'])
@custom.route('/_synapse/admin/v1/reset_password/<user_id>', methods=['POST'])
@custom.route('/_synapse/admin/v1/users/<user_id>/admin', methods=["PUT"])
@custom.route('/_synapse/admin/v2/users/<user_id>/delete_devices', methods=['POST'])
@custom.route('/_synapse/admin/v1/users/<user_id>/shadow_ban', methods=['DELETE', 'POST'])
@custom.route('/_synapse/admin/v1/users/<user_id>/override_ratelimit', methods=['GET', 'POST', 'DELETE'])
@custom.route('/_synapse/admin/v1/media/protect/<media_id>', methods=['POST'])
@custom.route('/_synapse/admin/v1/media/unprotect/<media_id>', methods=['POST'])
@custom.route('/_synapse/admin/v1/media/quarantine/<server_name>/<media_id>', methods=['POST'])
@custom.route('/_synapse/admin/v1/media/unquarantine/<server_name>/<media_id>', methods=['POST'])
@custom.route('/_dendrite/admin/purgeRoom/<roomId>', methods=['POST'])
@custom.route('/_dendrite/admin/refreshDevices/<userId>', methods=['POST'])
@custom.route('/_dendrite/admin/fulltext/reindex')
@custom.route("/_synapse/admin/v1/federation/destinations/<destination>/reset_connection", methods=["POST"])
@custom.route("/_synapse/admin/v1/rooms/<room>")
@custom.route("/_synapse/admin/v1/rooms/<room_id>/timestamp_to_event")
@custom.route("/_synapse/admin/v2/rooms/delete_status/<delete_id>")
@custom.route("/_synapse/admin/v1/rooms/<room_id_or_alias>/make_room_admin", methods=["POST"])
async def empty_response(**kwargs):
return jsonify({})
# Synapse # Synapse
@custom.route('/_synapse/admin/v1/server_version') @custom.route('/_synapse/admin/v1/server_version')
def synapse_version(): def synapse_version():
@@ -50,31 +73,28 @@ def synapse_user_info(user_id):
return jsonify({}), 201 return jsonify({}), 201
@custom.route('/_synapse/admin/v1/whois/<user_id>') @custom.route('/_synapse/admin/v1/whois/<user_id>')
def synapse_whois(user_id): async def synapse_whois(user_id):
return jsonify({"user_id":f"@vona:{server_name}","devices":{"":{"sessions":[{"connections":[{"ip":f"{the_funny_number}.{the_funny_number}.{the_funny_number}.{the_funny_number}","last_seen":the_funny_number,"user_agent":f"Vona/{vona_version}"},{"ip":f"{the_funny_number}.{the_funny_number}.{the_funny_number}.{the_funny_number}","last_seen":the_funny_number,"user_agent":f"Vona/{vona_version}"}]}]}}}) return jsonify({
"user_id": f"@vona:{server_name}",
@custom.route('/_synapse/admin/v1/deactivate/<user_id>', methods=['POST']) "devices": {
def synapse_deactivate(user_id): "": {
return jsonify({}) "sessions": [{
"connections": [{
@custom.route('/_synapse/admin/v1/suspend/<user_id>', methods=['PUT']) "ip":f"127.0.0.1",
def synapse_suspend(user_id): "last_seen":the_funny_number,
return jsonify({}) "user_agent":f"Vona/{vona_version}"
}]
@custom.route('/_synapse/admin/v1/reset_password/<user_id>', methods=['POST']) }]
def synapse_reset_pswd(user_id): }
return jsonify({}) }
})
@custom.route('/_synapse/admin/v1/users/<user_id>/admin', methods=['PUT'])
def synapse_change_admin(user_id):
return jsonify({})
@custom.route('/_synapse/admin/v1/users/<user_id>/joined_rooms') @custom.route('/_synapse/admin/v1/users/<user_id>/joined_rooms')
def synapse_user_joined_rooms(user_id): def synapse_user_joined_rooms(user_id):
return jsonify({"joined_rooms":[room_dir_room['chunk'][0]['room_id']],"total":1}) return jsonify({"joined_rooms":[room_dir_room['chunk'][0]['room_id']],"total":1})
@custom.route('/_synapse/admin/v1/users/<user_id>/sent_invite_count') @custom.route('/_synapse/admin/v1/users/<user_id>/sent_invite_count')
def synapse_invite_count(user_id): async def synapse_invite_count(user_id):
return jsonify({"invite_count": the_funny_number}) return jsonify({"invite_count": the_funny_number})
@custom.route('/_synapse/admin/v1/users/<user_id>/accountdata') @custom.route('/_synapse/admin/v1/users/<user_id>/accountdata')
@@ -99,43 +119,43 @@ def synapse_stupid_mas_bullshit(user_id):
@custom.route('/_synapse/admin/v2/users/<user_id>/devices', methods=['GET', 'POST']) @custom.route('/_synapse/admin/v2/users/<user_id>/devices', methods=['GET', 'POST'])
def synapse_device_list(user_id): def synapse_device_list(user_id):
if request.method == 'GET': if request.method == 'GET':
return jsonify({"devices":[{"device_id":"VVOONNAA","display_name":"Vona","last_seen_ip":"127.0.0.1","last_seen_ts":the_funny_number, "last_seen_user_agent": f"Vona/{vona_version}"}], "total": the_funny_number}) return jsonify({
"devices": [{
"device_id": "VVOONNAA",
"display_name": "Vona",
"last_seen_ip": "127.0.0.1",
"last_seen_ts": the_funny_number,
"last_seen_user_agent": f"Vona/{vona_version}"
}],
"total": 1
})
return jsonify({}) return jsonify({})
@custom.route('/_synapse/admin/v2/users/<user_id>/delete_devices', methods=['POST']) @custom.route('/_synapse/admin/v2/users/<user_id>/devices/<device_id>', methods=["GET", "PUT", "DELETE"])
def synapse_delete_devices(user_id): async def synapse_device_info(user_id, device_id):
return jsonify({})
@custom.route('/_synapse/admin/v2/users/<user_id>/devices/<device_id>')
def synapse_device_info(user_id, device_id):
if request.method == 'GET': if request.method == 'GET':
return jsonify({"device_id":"VVOONNAA","display_name":"Vona","last_seen_ip":"127.0.0.1","last_seen_ts":the_funny_number, "last_seen_user_agent": f"Vona/{vona_version}"}) return jsonify({
"device_id": "VVOONNAA",
"display_name": "Vona",
"last_seen_ip": "127.0.0.1",
"last_seen_ts": the_funny_number,
"last_seen_user_agent": f"Vona/{vona_version}"
})
return jsonify({}) return jsonify({})
@custom.route('/_synapse/admin/v1/users/<user_id>/pushers') @custom.route('/_synapse/admin/v1/users/<user_id>/pushers')
def synapse_pushers(user_id): async def synapse_pushers(user_id):
return jsonify({"pushers": [], "total": the_funny_number}) return jsonify({"pushers": [], "total": the_funny_number})
@custom.route('/_synapse/admin/v1/users/<user_id>/shadow_ban', methods=['DELETE', 'POST'])
def synapse_shadow_ban(user_id):
return jsonify({})
@custom.route('/_synapse/admin/v1/users/<user_id>/override_ratelimit', methods=['GET', 'POST', 'DELETE'])
def synapse_override_ratelimit(user_id):
return jsonify({})
@custom.route('/_synapse/admin/v1/username_available') @custom.route('/_synapse/admin/v1/username_available')
def synapse_username_available(): async def synapse_username_available():
return jsonify({"available": True}) return jsonify({"available": True})
@custom.route('/_synapse/admin/v1/auth_providers/<provider>/users/<ext>')
def synapse_auth_providers(provider, ext):
return jsonify({"user_id": f"@vona:{server_name}"})
@custom.route('/_synapse/admin/v1/threepid/<medium>/users/<addr>') @custom.route('/_synapse/admin/v1/threepid/<medium>/users/<addr>')
def synapse_threepid(medium, addr): @custom.route('/_synapse/admin/v1/auth_providers/<provider>/users/<ext>')
async def synapse_threepid(p, a):
return jsonify({"user_id": f"@vona:{server_name}"}) return jsonify({"user_id": f"@vona:{server_name}"})
@custom.route('/_synapse/admin/v1/<user_id>/redact') @custom.route('/_synapse/admin/v1/<user_id>/redact')
@@ -143,42 +163,36 @@ def synapse_redact(user_id):
return jsonify({"redact_id": os.urandom(16).hex()}) return jsonify({"redact_id": os.urandom(16).hex()})
@custom.route('/_synapse/admin/v1/user/redact_status/<redact_id>') @custom.route('/_synapse/admin/v1/user/redact_status/<redact_id>')
def synapse_redact_status(redact_id): async def synapse_redact_status(redact_id):
return jsonify({"status":"active","failed_redactions":[]}) return jsonify({"status":"active","failed_redactions":[]})
@custom.route('/_synapse/admin/v1/experimental_features/<user_id>', methods=['GET', 'PUT']) @custom.route('/_synapse/admin/v1/experimental_features/<user_id>', methods=['GET', 'PUT'])
def synapse_experimental_features(user_id): async def synapse_experimental_features(user_id):
return jsonify({"features": {}}) return jsonify({"features": {}})
@custom.route('/_synapse/admin/v1/register', methods=['GET', 'POST']) @custom.route('/_synapse/admin/v1/register', methods=['GET', 'POST'])
def synapse_register(): async def synapse_register():
if request.method == 'GET': if request.method == 'GET':
return jsonify({"nonce": os.urandom(16).hex()}) return jsonify({"nonce": os.urandom(16).hex()})
return jsonify({"access_token": f"@vona:{server_name}"}) return jsonify({"access_token": "vona"})
@custom.route('/_synapse/admin/v1/join/<roomId>', methods=['POST']) @custom.route('/_synapse/admin/v1/join/<roomId>', methods=['POST'])
def synapse_membership_manipulation(roomId): async def synapse_membership_manipulation(roomId):
return jsonify({"room_id": room_dir_room['chunk'][0]['room_id']}) return jsonify({"room_id": room_dir_room['chunk'][0]['room_id']})
@custom.route('/_synapse/admin/v1/account_validity/validity', methods=['POST']) @custom.route('/_synapse/admin/v1/account_validity/validity', methods=['POST'])
def synapse_account_validity(): async def synapse_account_validity():
return jsonify({"expiration_ts": the_funny_number}) return jsonify({"expiration_ts": the_funny_number})
@custom.route('/_synapse/admin/v1/send_server_notice', methods=['POST']) @custom.route('/_synapse/admin/v1/send_server_notice', methods=['POST'])
def synapse_server_notice(): @custom.route('/_synapse/admin/v1/send_server_notice/<txnId>', methods=["PUT"])
async def synapse_server_notice(**kwargs):
return jsonify({"event_id": make_event_id()}) return jsonify({"event_id": make_event_id()})
@custom.route('/_synapse/admin/v1/send_server_notice/<txnId>', methods=['PUT'])
def synapse_server_notice_second(txnId):
return jsonify({"event_id": make_event_id()})
@custom.route('/_synapse/admin/v1/purge_history/<room_id>', methods=['POST'])
def synapse_purge_room_history(room_id):
return jsonify({"purge_id": os.urandom(16).hex()})
@custom.route('/_synapse/admin/v1/purge_history/<room_id>/<event_id>', methods=['POST']) @custom.route('/_synapse/admin/v1/purge_history/<room_id>/<event_id>', methods=['POST'])
def synapse_purge_event(room_id, event_id): @custom.route('/_synapse/admin/v1/purge_history/<room_id>', methods=['POST'])
async def synapse_purge_event(**kwargs):
return jsonify({"purge_id": os.urandom(16).hex()}) return jsonify({"purge_id": os.urandom(16).hex()})
@custom.route('/_synapse/admin/v1/purge_history_status/<purge_id>') @custom.route('/_synapse/admin/v1/purge_history_status/<purge_id>')
@@ -189,122 +203,215 @@ def synapse_purge_status(purge_id):
def synapse_room_media(room_id): def synapse_room_media(room_id):
return jsonify({"local": [f"mxc://{server_name}/cat"], "remote": []}) return jsonify({"local": [f"mxc://{server_name}/cat"], "remote": []})
@custom.route('/_synapse/admin/v1/media/quarantine/<server_name>/<media_id>', methods=['POST'])
def synapse_quarantine_media(server_name, media_id):
return jsonify({})
@custom.route('/_synapse/admin/v1/media/unquarantine/<server_name>/<media_id>', methods=['POST'])
def synapse_unquarantine_media(server_name, media_id):
return jsonify({})
@custom.route('/_synapse/admin/v1/room/<room_id>/media/quarantine', methods=['POST']) @custom.route('/_synapse/admin/v1/room/<room_id>/media/quarantine', methods=['POST'])
def synapse_quarantine_room_media(room_id): def synapse_quarantine_room_media(room_id):
return jsonify({"num_quarantined": the_funny_number}) return jsonify({"num_quarantined": the_funny_number})
@custom.route('/_synapse/admin/v1/media/protect/<media_id>', methods=['POST'])
def synapse_protect_media(media_id):
return jsonify({})
@custom.route('/_synapse/admin/v1/media/unprotect/<media_id>', methods=['POST'])
def synapse_unprotect_media(media_id):
return jsonify({})
@custom.route('/_synapse/admin/v1/media/<server_name>/<media_id>', methods=['DELETE'])
def synapse_delete_single_media(media_id):
return jsonify({"deleted_media": ["cat"], "total": the_funny_number})
@custom.route('/_synapse/admin/v1/media/delete', methods=['POST'])
def synapse_delete_media():
return jsonify({"deleted_media": ["cat"], "total": the_funny_number})
@custom.route('/_synapse/admin/v1/media/<server_name>/delete', methods=['POST']) @custom.route('/_synapse/admin/v1/media/<server_name>/delete', methods=['POST'])
def synapse_delete_media_from_server(server_name): @custom.route('/_synapse/admin/v1/media/<server_name>/<media_id>', methods=['DELETE'])
@custom.route('/_synapse/admin/v1/media/delete', methods=['POST'])
async def synapse_delete_media_from_server(**kwargs):
return jsonify({"deleted_media": ["cat"], "total": the_funny_number}) return jsonify({"deleted_media": ["cat"], "total": the_funny_number})
@custom.route('/_synapse/admin/v1/purge_media_cache', methods=['POST']) @custom.route('/_synapse/admin/v1/purge_media_cache', methods=['POST'])
def synapse_delete_remote_media(): async def synapse_delete_remote_media():
return jsonify({"deleted": the_funny_number}) return jsonify({"deleted": the_funny_number})
@custom.route('/_synapse/admin/v1/statistics/users/media') @custom.route('/_synapse/admin/v1/statistics/users/media')
def synapse_media_stats(): async def synapse_media_stats():
return jsonify({"users":[{"displayname":"Vona","media_count":the_funny_number,"media_length":the_funny_number,"user_id":f"@vona:{server_name}"}],"total":the_funny_number}) return jsonify({"users":[{"displayname":"Vona","media_count":the_funny_number,"media_length":the_funny_number,"user_id":f"@vona:{server_name}"}],"total":the_funny_number})
@custom.route('/_synapse/admin/v1/statistics/database/rooms') @custom.route('/_synapse/admin/v1/statistics/database/rooms')
def synapse_room_stats(): async def synapse_room_stats():
return jsonify({"rooms": [{"room_id":room_dir_room['chunk'][0]['room_id'],"estimated_size":the_funny_number}]}) return jsonify({
"rooms": [{
@custom.route('/_synapse/admin/v1/background_updates/status') "room_id": room_dir_room['chunk'][0]['room_id'],
def synapse_background_upate_status(): "estimated_size": the_funny_number * 420
return jsonify({"enabled":False}) }]
})
@custom.route('/_synapse/admin/v1/background_updates/enabled', methods=['POST', 'GET']) @custom.route('/_synapse/admin/v1/background_updates/enabled', methods=['POST', 'GET'])
def synapse_change_bg_update(): @custom.route('/_synapse/admin/v1/background_updates/status')
async def synapse_change_bg_update():
return jsonify({"enabled":False}) return jsonify({"enabled":False})
# No documentation on what Synapse actually returns for this API, so a blank dict for now. # No documentation on what Synapse actually returns for this API, so a blank dict for now.
@custom.route('/_synapse/admin/v1/background_updates/start_job', methods=['POST']) @custom.route('/_synapse/admin/v1/background_updates/start_job', methods=['POST'])
def synapse_bg_update_start_job(): async def synapse_bg_update_start_job():
return jsonify({}) return jsonify({})
@custom.route('/_synapse/admin/v1/event_reports') @custom.route('/_synapse/admin/v1/event_reports')
def synapse_event_reports(): async def synapse_event_reports():
return jsonify({"event_reports": [{"event_id": make_event_id(),"id": the_funny_number,"reason": "vona","score": the_funny_number,"received_ts": the_funny_number,"room_id": room_dir_room['chunk'][0]['room_id'],"name": room_dir_room['chunk'][0]['name'],"sender": f"@vona:{server_name}","user_id": f"@vona:{server_name}"}],"total": the_funny_number}) return jsonify({
"event_reports": [{
"event_id": make_event_id(),
"id": the_funny_number,
"reason": "",
"score": the_funny_number,
"received_ts": the_funny_number,
"room_id": room_dir_room['chunk'][0]['room_id'],
"name": room_dir_room['chunk'][0]['name'],
"sender": f"@vona:{server_name}",
"user_id": f"@vona:{server_name}"
}],
"total": the_funny_number
})
@custom.route('/_synapse/admin/v1/event_reports/<report_id>', methods=['GET', 'DELETE']) @custom.route('/_synapse/admin/v1/event_reports/<report_id>', methods=['GET', 'DELETE'])
def synapse_interact_with_reported_event(report_id): async def synapse_interact_with_reported_event(report_id):
if request.method == 'GET': if request.method == 'GET':
return jsonify({"event_id": make_event_id(),"event_json": {"auth_events": [],"content": {"msgtype": "m.text","body": "Number 15: Burger King Foot Lettuce.\nThe last thing you'd want in your Burger King burger is someones foot fungus, but as it turns out, that might be what you get. A 4channer uploaded a photo, anonymously to the site showcasing his feet in a plastic bin of lettuce with the statement \"This is the lettuce you eat at Burger King.\". Admittedly, he had shoes on, but thats even worse. The post went live at 11:38 PM on July 16 and a mere 20 minutes later the Burger King in question was alerted to the rogue employee. At least, I hope hes rogue. How did it happen? Well, the BK employee hadn't removed the EXIF data from the uploaded photo, which suggested that the culprit was somewhere in Mayfield Heights, Ohio. This was at 11:47. 3 minutes later, at 11:50, the Burger King branch was posted with wishes of happy unemployment. 5 minutes later, the news station was contacted by another 4channer, and 3 minutes later at 11:58 a link was posted: BK's tell us about us online forum. The foot photo, otherwise known as Exhibit A, was attached. Cleveland Seen Magazine contacted the BK in question and the next day when questioned, the breakfast shift manager said \"Oh, I know who that is, hes getting fired\". Mystery solved, by 4chan. Now we can go back to eating our fast food in peace.","format": "org.matrix.custom.html","formatted_body": "Number 15: Burger King Foot Lettuce.<br />The last thing you'd want in your Burger King burger is someones foot fungus, but as it turns out, that might be what you get. A 4channer uploaded a photo, anonymously to the site showcasing his feet in a plastic bin of lettuce with the statement &quot;This is the lettuce you eat at Burger King.&quot;. Admittedly, he had shoes on, but thats even worse. The post went live at 11:38 PM on July 16 and a mere 20 minutes later the Burger King in question was alerted to the rogue employee. At least, I hope hes rogue. How did it happen? Well, the BK employee hadn't removed the EXIF data from the uploaded photo, which suggested that the culprit was somewhere in Mayfield Heights, Ohio. This was at 11:47. 3 minutes later, at 11:50, the Burger King branch was posted with wishes of happy unemployment. 5 minutes later, the news station was contacted by another 4channer, and 3 minutes later at 11:58 a link was posted: BK's tell us about us online forum. The foot photo, otherwise known as Exhibit A, was attached. Cleveland Seen Magazine contacted the BK in question and the next day when questioned, the breakfast shift manager said &quot;Oh, I know who that is, hes getting fired&quot;. Mystery solved, by 4chan. Now we can go back to eating our fast food in peace."},"depth": the_funny_number,"hashes": {"sha256": "todo: replace with something proper"},"origin": server_name,"origin_server_ts": the_funny_number,"prev_events": [make_event_id()],"prev_state": [],"room_id": room_dir_room['chunk'][0]['room_id'],"sender": f"@vona:{server_name}","signatures": {server_name: {"ed25519:a_JaEG": base64.b64encode(os.urandom(32)).decode('utf-8')[:87]}}},"type": "m.room.message","unsigned": {"age_ts": the_funny_number},"id": the_funny_number,"reason": "vona","score": the_funny_number,"received_ts": the_funny_number,"room_id": room_dir_room['chunk'][0]['room_id'],"name": room_dir_room['chunk'][0]['name'],"sender": f"@vona:{server_name}","user_id": f"@vona:{server_name}"}) return jsonify({"event_id": make_event_id(),"event_json": {"auth_events": [],"content": {"msgtype": "m.text","body": "Number 15: Burger King Foot Lettuce.\nThe last thing you'd want in your Burger King burger is someones foot fungus, but as it turns out, that might be what you get. A 4channer uploaded a photo, anonymously to the site showcasing his feet in a plastic bin of lettuce with the statement \"This is the lettuce you eat at Burger King.\". Admittedly, he had shoes on, but thats even worse. The post went live at 11:38 PM on July 16 and a mere 20 minutes later the Burger King in question was alerted to the rogue employee. At least, I hope hes rogue. How did it happen? Well, the BK employee hadn't removed the EXIF data from the uploaded photo, which suggested that the culprit was somewhere in Mayfield Heights, Ohio. This was at 11:47. 3 minutes later, at 11:50, the Burger King branch was posted with wishes of happy unemployment. 5 minutes later, the news station was contacted by another 4channer, and 3 minutes later at 11:58 a link was posted: BK's tell us about us online forum. The foot photo, otherwise known as Exhibit A, was attached. Cleveland Seen Magazine contacted the BK in question and the next day when questioned, the breakfast shift manager said \"Oh, I know who that is, hes getting fired\". Mystery solved, by 4chan. Now we can go back to eating our fast food in peace.","format": "org.matrix.custom.html","formatted_body": "Number 15: Burger King Foot Lettuce.<br />The last thing you'd want in your Burger King burger is someones foot fungus, but as it turns out, that might be what you get. A 4channer uploaded a photo, anonymously to the site showcasing his feet in a plastic bin of lettuce with the statement &quot;This is the lettuce you eat at Burger King.&quot;. Admittedly, he had shoes on, but thats even worse. The post went live at 11:38 PM on July 16 and a mere 20 minutes later the Burger King in question was alerted to the rogue employee. At least, I hope hes rogue. How did it happen? Well, the BK employee hadn't removed the EXIF data from the uploaded photo, which suggested that the culprit was somewhere in Mayfield Heights, Ohio. This was at 11:47. 3 minutes later, at 11:50, the Burger King branch was posted with wishes of happy unemployment. 5 minutes later, the news station was contacted by another 4channer, and 3 minutes later at 11:58 a link was posted: BK's tell us about us online forum. The foot photo, otherwise known as Exhibit A, was attached. Cleveland Seen Magazine contacted the BK in question and the next day when questioned, the breakfast shift manager said &quot;Oh, I know who that is, hes getting fired&quot;. Mystery solved, by 4chan. Now we can go back to eating our fast food in peace."},"depth": the_funny_number,"hashes": {"sha256": "todo: replace with something proper"},"origin": server_name,"origin_server_ts": the_funny_number,"prev_events": [make_event_id()],"prev_state": [],"room_id": room_dir_room['chunk'][0]['room_id'],"sender": f"@vona:{server_name}","signatures": {server_name: {"ed25519:a_JaEG": base64.b64encode(os.urandom(32)).decode('utf-8')[:87]}}},"type": "m.room.message","unsigned": {"age_ts": the_funny_number},"id": the_funny_number,"reason": "vona","score": the_funny_number,"received_ts": the_funny_number,"room_id": room_dir_room['chunk'][0]['room_id'],"name": room_dir_room['chunk'][0]['name'],"sender": f"@vona:{server_name}","user_id": f"@vona:{server_name}"})
else:
return jsonify({}) return jsonify({})
@custom.route("/_synapse/admin/v1/federation/destinations")
async def synapse_federation_destinations():
return jsonify({
"destinations": [{}],
"total": 0
})
@custom.route("/_synapse/admin/v1/federation/destinations/<destination>")
async def synapse_destination(destination):
return jsonify({
"destination": destination,
"retry_last_ts": the_funny_number,
"retry_interval": the_funny_number,
"failure_ts": the_funny_number,
"last_successful_stream_ordering": None
})
@custom.route("/_synapse/admin/v1/federation/destinations/<destination>/rooms")
async def synapse_destination_rooms(destination):
return jsonify({
"rooms": [],
"total": 0
})
@custom.route("/_synapse/admin/v1/registration_tokens")
async def synapse_reg_tokens():
return jsonify({
"registration_tokens": [{
"token": "Vona",
"uses_allowed": the_funny_number,
"pending": 0,
"completed": 1,
"expiry_time": None
}]
})
@custom.route("/_synapse/admin/v1/registration_tokens/<token>", methods=["GET", "PUT", "DELETE"])
async def synapse_reg_token(token):
if request.method == "DELETE":
return jsonify({})
return jsonify({
"token": "Vona",
"uses_allowed": the_funny_number,
"pending": 0,
"completed": 1,
"expiry_time": None
})
@custom.route("/_synapse/admin/v1/registration_tokens/new", methods=["POST"])
async def synapse_new_reg_token():
return jsonify({
"token": "Vona",
"uses_allowed": the_funny_number,
"pending": 0,
"completed": 1,
"expiry_time": None
})
@custom.route("/_synapse/admin/v1/rooms")
async def synapse_rooms():
return jsonify({
"rooms": [],
"offset": 0,
"total_rooms": 0
})
@custom.route("/_synapse/admin/v1/rooms/<room_id>/members")
async def synapse_room_members(room):
return jsonify({
"members": [
f"@vona:{server_name}"
],
"total": 1
})
@custom.route("/_synapse/admin/v1/rooms/<room_id>/state")
async def synapse_room_state(room):
return jsonify({"state": []})
@custom.route("/_synapse/admin/v1/rooms/<room_id>/state")
async def synapse_room_messages(room):
return jsonify({
"chunk": [],
"end": "vona",
"start": "vona"
})
@custom.route("/_synapse/admin/v1/rooms/<room_id>/block", methods=["GET", "PUT"])
async def synapse_block_room(room):
return jsonify({"block": False})
@custom.route("/_synapse/admin/v1/rooms/<room_id>", methods=["DELETE"])
async def synapse_room_delete(room):
return jsonify({
"kicked_users": [
f"@vona:{server_name}"
],
"failed_to_kick_users": [],
"local_aliases": [],
"new_room_id": f"!vona:{server_name}"
})
@custom.route("/_synapse/admin/v2/rooms/<room_id>", methods=["DELETE"])
async def synapse_room_delete_v2(room):
return jsonify({"delete_id": "vona"})
@custom.route("/_synapse/admin/v2/rooms/<room_id>/delete_status")
async def synapse_room_delete_status(room):
return jsonify({"results": []})
@custom.route("/_synapse/admin/v1/rooms/<room_id_or_alias>/forward_extremities", methods=["GET"])
async def synapse_forward_extremities(room):
if request.method == "DELETE":
return jsonify({"deleted": 0})
return jsonify({
"count": 1,
"results": [{
"event_id": make_event_id(),
"state_group": the_funny_number,
"depth": the_funny_number,
"received_ts": the_funny_number
}]
})
# Dendrite - https://element-hq.github.io/dendrite/administration/adminapi # Dendrite - https://element-hq.github.io/dendrite/administration/adminapi
@custom.route('/_dendrite/admin/evacuateUser/<userId>', methods=['POST']) @custom.route('/_dendrite/admin/evacuateUser/<userId>', methods=['POST'])
def dendrite_evacuate_user(userId): async def dendrite_evacuate_user(userId):
return jsonify({"affected":[room_dir_room['chunk'][0]['room_id']]}) return jsonify({"affected": [room_dir_room['chunk'][0]['room_id']]})
@custom.route('/_dendrite/admin/evacuateRoom/<roomId>', methods=['POST']) @custom.route('/_dendrite/admin/evacuateRoom/<roomId>', methods=['POST'])
def dendrite_evacuate_room(roomId): async def dendrite_evacuate_room(roomId):
return jsonify({"affected":[f"@vona:{server_name}"]}) return jsonify({"affected": [f"@vona:{server_name}"]})
@custom.route('/_dendrite/admin/resetPassword/<userId>', methods=['POST']) @custom.route('/_dendrite/admin/resetPassword/<userId>', methods=['POST'])
def dendrite_reset_pswd(userId): async def dendrite_reset_pswd(userId):
return jsonify({"password_updated":true}) return jsonify({"password_updated": True})
@custom.route('/_dendrite/admin/purgeRoom/<roomId>', methods=['POST']) # Conduwuit/Tuwunel/Continuwuity
def dendrite_purge_room(roomId):
return jsonify({})
@custom.route('/_dendrite/admin/refreshDevices/<userId>', methods=['POST'])
def dendrite_refresh_devices(userId):
return jsonify({})
@custom.route('/_dendrite/admin/fulltext/reindex')
def dendrite_reindex():
return jsonify({})
# Conduwuit
@custom.route('/_conduwuit/local_user_count')
def conduwuit_user_count():
return jsonify({"count": 1})
@custom.route('/_conduwuit/server_version')
def conduwuit_server_version():
return jsonify({"name":"Vona","version":vona_version})
# Continuwuity
@custom.route('/_continuwuity/local_user_count') @custom.route('/_continuwuity/local_user_count')
def continuwuity_user_count(): @custom.route('/_conduwuit/local_user_count')
@custom.route('/_tuwunel/local_user_count')
async def conduwuit_user_count():
return jsonify({"count": 1}) return jsonify({"count": 1})
@custom.route('/_continuwuity/server_version') @custom.route('/_continuwuity/server_version')
def continuwuity_server_version(): @custom.route('/_conduwuit/server_version')
return jsonify({"name":"Vona","version":vona_version})
# Tuwunel
@custom.route('/_tuwunel/local_user_count')
def tuwunel_user_count():
return jsonify({"count": 1})
@custom.route('/_tuwunel/server_version') @custom.route('/_tuwunel/server_version')
def tuwunel_server_version(): async def conduwuit_server_version():
return jsonify({"name":"Vona","version":vona_version}) return jsonify({"name":"Vona","version":vona_version})

View File

@@ -2,12 +2,12 @@ import nacl.signing
import hashlib import hashlib
import base64 import base64
import config import config
import random
import copy import copy
import json import json
import re import re
import os
vona_version = "1.3.0" vona_version = "1.4.0"
def canonical_json(value): def canonical_json(value):
@@ -81,15 +81,17 @@ def sign_json_without_discard(data):
return data return data
def make_event_id(): def make_event_id(seed=None):
event_id = "$" if seed is not None:
random.seed(seed)
random_bytes = bytearray(random.getrandbits(8) for _ in range(32))
event_id = "$"
event_id += re.sub( event_id += re.sub(
r"[\/+=]", r"[\/+=]",
"_", "_",
base64.b64encode( base64.b64encode(random_bytes).decode("utf-8"),
os.urandom(32),
).decode("utf-8"),
).rstrip("=")[:44] ).rstrip("=")[:44]
event_id += ":" + config.server_name event_id += ":" + config.server_name

View File

@@ -2,7 +2,7 @@ from flask import Blueprint, jsonify, request
from config import server_name, the_funny_number from config import server_name, the_funny_number
import time import time
identity = Blueprint("matrix_identity", __name__) identity = Blueprint("identity", __name__)
# This implements being an identity server. # This implements being an identity server.
# I'm pretty sure only Element uses this, # I'm pretty sure only Element uses this,
@@ -12,7 +12,19 @@ identity = Blueprint("matrix_identity", __name__)
@identity.route("/_matrix/identity/versions") @identity.route("/_matrix/identity/versions")
async def versions(): async def versions():
# Stolen from the vector.im identity server # Stolen from the vector.im identity server
return jsonify({"versions":["r0.1.0","r0.2.0","r0.2.1","r0.3.0","v1.1","v1.2","v1.3","v1.4","v1.5"]}) return jsonify({
"versions": [
"r0.1.0",
"r0.2.0",
"r0.2.1",
"r0.3.0",
"v1.1",
"v1.2",
"v1.3",
"v1.4",
"v1.5"
]
})
# https://spec.matrix.org/v1.16/identity-service-api/#authentication # https://spec.matrix.org/v1.16/identity-service-api/#authentication
@@ -20,10 +32,12 @@ async def versions():
async def account_info(): async def account_info():
return jsonify({"user_id": f"@vona:{server_name}"}) return jsonify({"user_id": f"@vona:{server_name}"})
@identity.route("/_matrix/identity/v2/account/logout", methods=['POST']) @identity.route("/_matrix/identity/v2/account/logout", methods=['POST'])
async def logout(): async def logout():
return jsonify({}) return jsonify({})
@identity.route("/_matrix/identity/v2/account/register", methods=['POST']) @identity.route("/_matrix/identity/v2/account/register", methods=['POST'])
async def register(): async def register():
return jsonify({"token":"vona"}) return jsonify({"token":"vona"})
@@ -37,28 +51,22 @@ async def policies():
return jsonify({}) return jsonify({})
# https://spec.matrix.org/v1.16/identity-service-api/#status-check
@identity.route('/_matrix/identity/v2') @identity.route('/_matrix/identity/v2')
async def status(): async def status():
return jsonify({}) return jsonify({})
# https://spec.matrix.org/v1.16/identity-service-api/#key-management
@identity.route('/_matrix/identity/v2/pubkey/ephemeral/isvalid') @identity.route('/_matrix/identity/v2/pubkey/ephemeral/isvalid')
async def pubkey_eph_validity():
return jsonify({"valid":True})
@identity.route('/_matrix/identity/v2/pubkey/isvalid') @identity.route('/_matrix/identity/v2/pubkey/isvalid')
async def pubkey_validity(): async def pubkey_validity():
return jsonify({"valid":True}) return jsonify({"valid": True})
@identity.route('/_matrix/identity/v2/pubkey/<key>') @identity.route('/_matrix/identity/v2/pubkey/<key>')
async def get_key(key): async def get_key(key):
return jsonify({"errcode":"M_NOT_FOUND","error":"The public key was not found"}), 404 return jsonify({
"errcode": "M_NOT_FOUND",
"error": "The public key was not found"
}), 404
# https://spec.matrix.org/v1.16/identity-service-api/#association-lookup
@identity.route('/_matrix/identity/v2/hash_details') @identity.route('/_matrix/identity/v2/hash_details')
async def hash_details(): async def hash_details():
return jsonify({"algorithms":["none","sha256"],"lookup_pepper": "vona"}) return jsonify({"algorithms":["none","sha256"],"lookup_pepper": "vona"})
@@ -72,23 +80,15 @@ async def lookup():
else: else:
return jsonify({"errcode": "M_INVALID_PEPPER","error": "Invalid pepper"}) return jsonify({"errcode": "M_INVALID_PEPPER","error": "Invalid pepper"})
# https://spec.matrix.org/v1.16/identity-service-api/#establishing-associations
@identity.route('/_matrix/identity/v2/validate/email/requestToken', methods=['POST']) @identity.route('/_matrix/identity/v2/validate/email/requestToken', methods=['POST'])
async def request_email_token(): @identity.route('/_matrix/identity/v2/validate/msisdn/requestToken', methods=['POST'])
return jsonify({"sid":str(the_funny_number)}) async def request_validation_token():
return jsonify({"sid": str(the_funny_number)})
@identity.route('/_matrix/identity/v2/validate/email/submitToken', methods=['GET', 'POST']) @identity.route('/_matrix/identity/v2/validate/email/submitToken', methods=['GET', 'POST'])
async def submit_email_token():
return jsonify({"success":True})
@identity.route('/_matrix/identity/v2/validate/msisdn/requestToken', methods=['POST'])
async def request_phone_token():
return jsonify({"sid":str(the_funny_number)})
@identity.route('/_matrix/identity/v2/validate/msisdn/submitToken', methods=['GET', 'POST']) @identity.route('/_matrix/identity/v2/validate/msisdn/submitToken', methods=['GET', 'POST'])
async def submit_phone_token(): async def submit_validation_token():
return jsonify({"success":True}) return jsonify({"success": True})
@identity.route('/_matrix/identity/v2/3pid/bind', methods=['POST']) @identity.route('/_matrix/identity/v2/3pid/bind', methods=['POST'])
async def threepid_bind(): async def threepid_bind():
@@ -113,30 +113,41 @@ async def threepid_unbind():
@identity.route('/_matrix/identity/v2/3pid/getValidated3pid') @identity.route('/_matrix/identity/v2/3pid/getValidated3pid')
async def threepid_validated(): async def threepid_validated():
# Please email abuse@matrix.org # Please email abuse@matrix.org
return jsonify({
return jsonify({"address":"abuse@matrix.org","medium":"email","validated_at":the_funny_number}) "address": "abuse@matrix.org",
"medium": "email",
"validated_at": the_funny_number
})
# https://spec.matrix.org/v1.16/identity-service-api/#invitation-storage # https://spec.matrix.org/v1.16/identity-service-api/#invitation-storage
@identity.route('/_matrix/identity/v2/store-invite', methods=['POST']) @identity.route('/_matrix/identity/v2/store-invite', methods=['POST'])
async def invite(): async def invite():
return jsonify({"display_name":"Vona","public_keys":[{"key_validity_url":"https://example.com/_matrix/identity/v2/pubkey/isvalid","public_key":"ohyeah"},{"key_validity_url":"https://example.com/_matrix/identity/v2/pubkey/ephemeral/isvalid","public_key":"thisssssss"}],"token":"vona"}) return jsonify({
"display_name": "Vona",
"public_keys": [
{
"key_validity_url": f"https://{server_name}/_matrix/identity/v2/pubkey/isvalid",
"public_key":"ohyeah"
},
{
"key_validity_url": f"https://{server_name}/_matrix/identity/v2/pubkey/ephemeral/isvalid",
"public_key":"thisssssss"
}
],
"token": "vona"
})
# https://spec.matrix.org/v1.16/identity-service-api/#ephemeral-invitation-signing # https://spec.matrix.org/v1.16/identity-service-api/#ephemeral-invitation-signing
@identity.route('/_matrix/identity/v2/sign-ed25519', methods=['POST']) @identity.route('/_matrix/identity/v2/sign-ed25519', methods=['POST'])
async def invite_signing(): async def invite_signing():
# We don't want to sign any proivided
# JSON, thus make sure the keys match
required_keys = {'mxid', 'private_key', 'token'} required_keys = {'mxid', 'private_key', 'token'}
d = data.get_json() d = data.get_json()
if set(d.keys()) == required_keys: if set(d.keys()) == required_keys:
return jsonify(sign_json(d)) return jsonify(sign_json(d))
else: else:
# User submitted invalid data, reject
return jsonify({ return jsonify({
"errcode": "M_UNRECOGNIZED", "errcode": "M_UNRECOGNIZED",
"error": "Didn't recognize token" "error": "Didn't recognize token"

View File

@@ -23,12 +23,22 @@ app.register_blueprint(custom)
app.register_blueprint(server) app.register_blueprint(server)
app.register_blueprint(apps) app.register_blueprint(apps)
@app.before_request
async def preflight():
if request.method == "OPTIONS":
return "", 204
@app.after_request @app.after_request
async def handle_logging(response): async def handle_logging(response):
if request.method == "OPTIONS":
# Discard logs for OPTIONS
return response
try: try:
if "Authorization" in request.headers: if "Authorization" in request.headers:
if request.headers["Authorization"].split()[0] == "X-Matrix": if request.headers["Authorization"].split()[0] == "X-Matrix":
origin = request.headers["Authorization"].split('origin="')[1].split('"')[0] origin = request.headers["Authorization"].split('origin="')[1].split('"')[0]
else: else:
origin = "client" origin = "client"
else: else:
@@ -36,9 +46,16 @@ async def handle_logging(response):
except: except:
origin = "unknown" origin = "unknown"
print(f"[{origin}] [{request.remote_addr}] [{datetime.now().strftime('%d/%b/%Y:%H:%M:%S')}] {request.method} {request.path} {response.status_code}") print(f"[{origin}] [{request.remote_addr}] [{datetime.now().strftime('%d/%b/%Y:%H:%M:%S')}] {request.method} {request.full_path} {response.status_code}")
response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
response.headers["Access-Control-Allow-Origin"] = "*"
response.headers["Access-Control-Allow-Headers"] = "*"
response.headers["Access-Control-Allow-Methods"] = "GET, HEAD, POST, PUT, DELETE, OPTIONS"
return response return response
# Landing page # Landing page
@app.route("/") @app.route("/")
async def root(): async def root():

View File

@@ -1,29 +1,28 @@
from flask import jsonify, Blueprint, request from flask import jsonify, Blueprint, request
policy = Blueprint("policy_server", __name__) policy = Blueprint("policy", __name__)
matrix_org = [ matrix_org = [
"dendrite.matrix.org", "dendrite.matrix.org",
"beta.matrix.org",
"matrix.org", "matrix.org",
"element.io", "element.io",
"t2l.io", "t2bot.io",
"t2bot.io" "t2l.io"
] ]
@policy.route("/_matrix/policy/unstable/org.matrix.msc4284/event/<eventId>/check", methods=["POST"])
@policy.route("/_matrix/policy/v1/event/<eventId>/check", methods=["POST"]) @policy.route("/_matrix/policy/v1/event/<eventId>/check", methods=["POST"])
def check_event(eventId): async def check_event(eventId):
if request.get_json()["origin"] in matrix_org: if request.get_json()["origin"] in matrix_org:
return jsonify({"recommendation": "spam"}) return jsonify({"recommendation": "spam"})
else:
return jsonify({"recommendation": "ok"}) return jsonify({"recommendation": "ok"})
@policy.route(
"/_matrix/policy/unstable/org.matrix.msc4284/event/<eventId>/check", @policy.route("/_matrix/policy/unstable/org.matrix.msc4284/sign", methods=["POST"])
methods=["POST"], @policy.route("/_matrix/policy/v1/sign", methods=["POST"])
) async def sign_event():
def check_event_unstable(eventId): # NOTE: trolled
if request.get_json()["origin"] in matrix_org: return jsonify({})
return jsonify({"recommendation": "spam"})
else:
return jsonify({"recommendation": "ok"})

View File

@@ -6,22 +6,20 @@ import json
import time import time
import os import os
server = Blueprint('matrix_server', __name__) server = Blueprint("server", __name__)
# NOTE: Synapse rejects this for
# whatever reason. Still looking def send_join(request, roomId) -> dict:
# into this issue.
def send_join(request, roomId):
event_chain = [] event_chain = []
event_hashes = [] event_hashes = []
event_ids = [ event_ids = [
globals.make_event_id(), globals.make_event_id(seed=f"1_{roomId}"),
globals.make_event_id(), globals.make_event_id(seed=f"2_{roomId}"),
globals.make_event_id(), globals.make_event_id(seed=f"3_{roomId}"),
globals.make_event_id(), globals.make_event_id(seed=f"4_{roomId}"),
globals.make_event_id(), globals.make_event_id(seed=f"5_{roomId}"),
globals.make_event_id(), globals.make_event_id(seed=f"6_{roomId}"),
] ]
create_event = { create_event = {
@@ -214,10 +212,6 @@ def send_join(request, roomId):
event_chain.append(shistory) event_chain.append(shistory)
remote_join = request.get_json() remote_join = request.get_json()
remote_join["auth_events"] = sguest_access["auth_events"]
remote_join["prev_events"] = [[shistory["event_id"], shistory["hashes"]]]
remote_join["depth"] = 7
remote_join = globals.hash_and_sign_event(remote_join)
response = { response = {
"auth_chain": event_chain, "auth_chain": event_chain,
@@ -227,18 +221,20 @@ def send_join(request, roomId):
"state": event_chain "state": event_chain
} }
# debug statement
print(json.dumps(response, indent='\t'))
return response return response
@server.route('/_matrix/federation/v1/version') @server.route("/_matrix/federation/v1/version")
def version(): async def version():
return jsonify({"server": {"version": globals.vona_version,"name": "Vona"}}) return jsonify({
"server": {
"version": globals.vona_version,
"name": "Vona"
}
})
@server.route('/_matrix/key/v2/server') @server.route("/_matrix/key/v2/server")
def keys(): async def keys():
return jsonify(globals.sign_json({ return jsonify(globals.sign_json({
"old_verify_keys": {}, "old_verify_keys": {},
"server_name": server_name, "server_name": server_name,
@@ -251,14 +247,14 @@ def keys():
})) }))
@server.route('/_matrix/federation/v1/query/directory') @server.route('/_matrix/federation/v1/query/directory')
def room_query(): async def room_query():
return jsonify({ return jsonify({
"room_id": room_dir_room['chunk'][0]['room_id'], "room_id": globals.make_event_id().replace("$", "!"),
"servers": [server_name] "servers": [server_name]
}) })
@server.route('/_matrix/federation/v1/media/download/<media_id>') @server.route('/_matrix/federation/v1/media/download/<media_id>')
def download_media(media_id): async def download_media(media_id):
# Auth media requires this to be # Auth media requires this to be
# multipart despite not even using # multipart despite not even using
# it for anything. Minor annoyance. # it for anything. Minor annoyance.
@@ -280,44 +276,85 @@ def download_media(media_id):
return response return response
@server.route('/_matrix/federation/v1/media/thumbnail/<media_id>') @server.route("/_matrix/federation/v1/media/thumbnail/<media_id>")
def thumbnail_media(media_id): async def thumbnail_media(media_id):
return jsonify({"errcode": "M_TOO_CUTE","error": "Cat is too cute to thumbnail"}), 403 return jsonify({
"errcode": "M_TOO_CUTE",
"error": "Cat is too cute to thumbnail"
}), 418
@server.route('/_matrix/federation/v1/send_join/<roomId>/<eventId>', methods=['PUT']) @server.route("/_matrix/federation/v1/send_join/<roomId>/<eventId>", methods=["PUT"])
def send_join_v1(roomId, eventId): async def send_join_v1(roomId, eventId):
return jsonify([200, send_join(request, roomId)]) return jsonify([200, send_join(request, roomId)])
@server.route('/_matrix/federation/v2/send_join/<roomId>/<eventId>', methods=['PUT']) @server.route("/_matrix/federation/v2/send_join/<roomId>/<eventId>", methods=["PUT"])
def send_join_v2(roomId, eventId): async def send_join_v2(roomId, eventId):
return jsonify(send_join(request, roomId)) return jsonify(send_join(request, roomId))
@server.route('/_matrix/federation/v1/make_join/<roomId>/<userId>') @server.route("/_matrix/federation/v1/make_join/<roomId>/<userId>")
def make_join(roomId, userId): async def make_join(roomId, userId):
if roomId.split(":")[1] != server_name:
return jsonify({ return jsonify({
"event": { "errcode": "M_FORBIDDEN",
"error": "You are not invited to this room."
}), 403
class bullshit:
def get_json():
return {}
state = send_join(
request=bullshit,
roomId=roomId
)["state"]
join = {
"content": { "content": {
"join_authorised_via_users_server": f"@vona:{server_name}", "join_authorised_via_users_server": f"@vona:{server_name}",
"membership": "join" "membership": "join"
}, },
"origin": server_name, "origin": server_name,
"origin_server_ts": str(the_funny_number), "origin_server_ts": 7,
"room_id": roomId, "room_id": roomId,
"sender": userId, "sender": userId,
"state_key": userId, "state_key": userId,
"type": "m.room.member" "type": "m.room.member",
}, "depth": 7
}
join["auth_events"] = [
[
state[0]["event_id"],
state[0]["hashes"]
],
[
state[2]["event_id"],
state[2]["hashes"]
],
[
state[3]["event_id"],
state[3]["hashes"]
]
]
join["prev_events"] = [[
state[5]["event_id"],
state[5]["hashes"]
]]
return jsonify({
"event": globals.hash_and_sign_event(join),
"room_version": "2" "room_version": "2"
}) })
@server.route('/_matrix/federation/v1/publicRooms', methods=['POST', 'GET']) @server.route('/_matrix/federation/v1/publicRooms', methods=['POST', 'GET'])
def room_directory(): async def room_directory():
return jsonify(room_dir_room) return jsonify(room_dir_room)
# https://spec.matrix.org/v1.16/server-server-api/#transactions # https://spec.matrix.org/v1.16/server-server-api/#transactions
@server.route('/_matrix/federation/v1/send/<txnId>', methods=['PUT']) @server.route('/_matrix/federation/v1/send/<txnId>', methods=["PUT"])
def receive_txn(txnId): async def receive_txn(txnId):
# We will need to implement a way to store every # We will need to implement a way to store every
# event we need if we want to send events in the # event we need if we want to send events in the
# future. We don't send events currently, however. # future. We don't send events currently, however.
@@ -344,7 +381,7 @@ def receive_txn(txnId):
return jsonify(response) return jsonify(response)
@server.route('/_matrix/federation/v1/query/profile') @server.route('/_matrix/federation/v1/query/profile')
def user_profile(): async def user_profile():
field = request.args.get('field') field = request.args.get('field')
if field: if field:
if field == 'avatar_url': if field == 'avatar_url':
@@ -358,6 +395,7 @@ def user_profile():
# https://spec.matrix.org/v1.16/server-server-api/#device-management # https://spec.matrix.org/v1.16/server-server-api/#device-management
@server.route(f'/_matrix/federation/v1/user/devices/@/:{server_name}')
@server.route('/_matrix/federation/v1/user/devices/<user>') @server.route('/_matrix/federation/v1/user/devices/<user>')
async def user_devices(user): async def user_devices(user):
return jsonify({ return jsonify({
@@ -366,28 +404,30 @@ async def user_devices(user):
"user_id": f"@vona:{server_name}" "user_id": f"@vona:{server_name}"
}) })
@server.route(f'/_matrix/federation/v1/user/devices/@/:{server_name}')
async def user_devices_fuck_flask():
return jsonify({
"devices": [],
"stream_id": the_funny_number,
"user_id": f"@vona:{server_name}"
})
@server.route('/_matrix/federation/v1/user/keys/query', methods=['POST']) @server.route('/_matrix/federation/v1/user/keys/query', methods=['POST'])
def user_keys(): async def user_keys():
return jsonify({"device_keys":{f"@vona:{server_name}":{}}}) try:
users = request.json["device_keys"]
except:
return jsonify({
"errcode": "M_NOT_FOUND",
"error": "User does not exist"
}), 404
return jsonify({"device_keys": users})
# https://spec.matrix.org/v1.16/server-server-api/#inviting-to-a-room @server.route('/_matrix/federation/v2/invite/<room>/<txnId>', methods=["PUT"])
@server.route('/_matrix/federation/v2/invite/<room>/<txnId>', methods=['PUT']) async def invite_user_v2(room, txnId):
def invite_user_v2(room, txnId):
return invite_user(request.data) return invite_user(request.data)
@server.route('/_matrix/federation/v1/invite/<room>/<txnId>', methods=['PUT'])
def invite_user_v1(room, txnId): @server.route('/_matrix/federation/v1/invite/<room>/<txnId>', methods=["PUT"])
async def invite_user_v1(room, txnId):
return [200, invite_user(request.data)] return [200, invite_user(request.data)]
def invite_user(data): def invite_user(data):
try: try:
invite_data = json.loads(data) invite_data = json.loads(data)