Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
a5b34fb4a8
|
|||
|
2212453a96
|
|||
|
d9583f3472
|
|||
|
3d9ff622fd
|
|||
|
945f92e25f
|
|||
|
29ecfc0387
|
|||
|
4bdeca6139
|
|||
|
b8f8a1a7b1
|
|||
|
35896ed1ee
|
|||
|
da82f492e8
|
|||
|
17d5a6458e
|
|||
|
ad4c5cc5d2
|
|||
|
8999a5e414
|
|||
|
2fa647f7e7
|
|||
|
1c87e8b5a5
|
|||
|
9d68082764
|
|||
|
32cdb239ff
|
|||
|
626c865461
|
|||
|
fe5c0e23b5
|
|||
|
09cb708594
|
|||
|
ad72886fb3
|
18
.pre-commit-config.yaml
Normal file
18
.pre-commit-config.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
default_install_hook_types:
|
||||
- pre-commit
|
||||
- commit-msg
|
||||
default_stages:
|
||||
- pre-commit
|
||||
- manual
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.14.2
|
||||
hooks:
|
||||
- id: ruff-check
|
||||
args: [ "--fix" ]
|
||||
|
||||
- repo: https://foundry.fsky.io/vel/ty-pre-commit
|
||||
rev: 0.0.1-alpha.25
|
||||
hooks:
|
||||
- id: ty-check
|
||||
5
MSCs.md
5
MSCs.md
@@ -22,13 +22,16 @@ Merged MSCs:
|
||||
|
||||
Non-merged MSCs:
|
||||
|
||||
* [MSC2666: Get rooms in common with another user](https://github.com/matrix-org/matrix-spec-proposals/pull/2666)
|
||||
* [MSC4358: Out-of-room server discovery](https://github.com/matrix-org/matrix-spec-proposals/pull/4358)
|
||||
* [MSC4367: via routes in the published room directory](https://github.com/matrix-org/matrix-spec-proposals/pull/4367)
|
||||
* [MSC4370: Federation endpoint for retrieving current extremities](https://github.com/matrix-org/matrix-spec-proposals/pull/4370)
|
||||
* [MSC4373: Server opt-out of specific EDU types](https://github.com/matrix-org/matrix-spec-proposals/pull/4373)
|
||||
* [MSC4375: Admin Room Management](https://github.com/matrix-org/matrix-spec-proposals/pull/4375)
|
||||
|
||||
|
||||
Room version MSCs:
|
||||
|
||||
* [MSC1759: Room V2](https://github.com/matrix-org/matrix-spec-proposals/blob/main/proposals/1759-rooms-v2.md)
|
||||
* [MSC1659: Room V3](https://github.com/matrix-org/matrix-spec-proposals/blob/main/proposals/1659-event-id-as-hashes.md)
|
||||
* [MSC2002: Room V4](https://github.com/matrix-org/matrix-spec-proposals/blob/main/proposals/2002-rooms-v4.md)
|
||||
@@ -37,3 +40,5 @@ Room version MSCs:
|
||||
* [MSC2998: Room V7](https://github.com/matrix-org/matrix-spec-proposals/blob/main/proposals/2998-rooms-v7.md)
|
||||
* [MSC3289: Room V8](https://github.com/matrix-org/matrix-spec-proposals/blob/main/proposals/3289-rooms-v8.md)
|
||||
* [MSC3375: Room V9](https://github.com/matrix-org/matrix-spec-proposals/blob/main/proposals/3375-room-v9.md)
|
||||
* [MSC3604: Room V10](https://github.com/matrix-org/matrix-spec-proposals/blob/main/proposals/3604-rooms-v10.md)
|
||||
* [MSC3820: Room V11](https://github.com/matrix-org/matrix-spec-proposals/blob/main/proposals/3820-rooms-v11.md)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name = "vona"
|
||||
description = "Flazing bast Matrix homeserver"
|
||||
license = {text = "Velicense"}
|
||||
requires-python = ">=3.12"
|
||||
requires-python = "<4,>=3.12"
|
||||
|
||||
dependencies = [
|
||||
"httpx (>=0.28.1,<0.29.0)",
|
||||
|
||||
@@ -78,7 +78,7 @@ async def handle_logging(response):
|
||||
else:
|
||||
origin = "client"
|
||||
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if request.path.startswith("/.well-known/matrix/"):
|
||||
|
||||
@@ -6,6 +6,9 @@ import asyncio
|
||||
import random
|
||||
import os
|
||||
|
||||
from .groups import groups
|
||||
from .admin import admin
|
||||
|
||||
from flask import (
|
||||
Blueprint,
|
||||
jsonify,
|
||||
@@ -15,9 +18,8 @@ from flask import (
|
||||
|
||||
client = Blueprint("client", __name__)
|
||||
|
||||
from .groups import groups
|
||||
|
||||
client.register_blueprint(groups)
|
||||
client.register_blueprint(admin)
|
||||
|
||||
|
||||
@client.route("/_matrix/client/v3/account/password", methods=["POST"])
|
||||
@@ -60,58 +62,15 @@ async def spec_versions():
|
||||
["r0.6.1"] + [f"v1.{i}" for i in range(1, 17)]
|
||||
),
|
||||
"unstable_features": {
|
||||
"uk.half-shot.msc2666.query_mutual_rooms": True,
|
||||
"uk.half-shot.msc2666.mutual_rooms": True,
|
||||
"uk.half-shot.msc2666": True,
|
||||
"uk.timedout.msc4323": True
|
||||
"uk.timedout.msc4323": True,
|
||||
"uk.timedout.msc4375": True,
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@client.route("/_matrix/client/v3/admin/whois/<user>")
|
||||
@client.route("/_matrix/client/r0/admin/whois/<user>")
|
||||
async def whois(user):
|
||||
if user.startswith("@"):
|
||||
return jsonify({
|
||||
"devices": {
|
||||
"": {
|
||||
"sessions": [{
|
||||
"connections": [{
|
||||
"ip": "127.0.0.1",
|
||||
"last_seen": config.the_funny_number,
|
||||
"user_agent": f"Vona/{globals.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/api/v1/rooms/<room>/members")
|
||||
@client.route("/_matrix/client/v3/rooms/<room>/members")
|
||||
@client.route("/_matrix/client/r0/rooms/<room>/members")
|
||||
@@ -159,7 +118,7 @@ async def register():
|
||||
"device_id": "VVOONNAA"
|
||||
})
|
||||
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return jsonify({
|
||||
@@ -342,7 +301,7 @@ async def sync():
|
||||
if "timeout" in request.args:
|
||||
try:
|
||||
wait_time = int(request.args.get("timeout")) / 1000
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
await asyncio.sleep(wait_time)
|
||||
|
||||
@@ -420,7 +379,7 @@ async def events():
|
||||
if "timeout" in request.args:
|
||||
try:
|
||||
await asyncio.sleep(int(request.args["timeout"]) / 1000)
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return jsonify({
|
||||
|
||||
153
vona/client/admin.py
Normal file
153
vona/client/admin.py
Normal file
@@ -0,0 +1,153 @@
|
||||
import vona.globals as globals
|
||||
import vona.config as config
|
||||
|
||||
from vona.federation import (
|
||||
send_join,
|
||||
bullshit,
|
||||
)
|
||||
|
||||
from flask import (
|
||||
Blueprint,
|
||||
jsonify,
|
||||
request,
|
||||
)
|
||||
|
||||
admin = Blueprint("admin", __name__)
|
||||
|
||||
# This implements misc standard admin APIs.
|
||||
|
||||
|
||||
@admin.route("/_matrix/client/v3/admin/whois/<user>")
|
||||
@admin.route("/_matrix/client/r0/admin/whois/<user>")
|
||||
async def whois(user):
|
||||
if user.startswith("@"):
|
||||
return jsonify({
|
||||
"devices": {
|
||||
"": {
|
||||
"sessions": [{
|
||||
"connections": [{
|
||||
"ip": "127.0.0.1",
|
||||
"last_seen": config.the_funny_number,
|
||||
"user_agent": f"Vona/{globals.version}"
|
||||
}]
|
||||
}]
|
||||
}
|
||||
},
|
||||
"user_id": user
|
||||
})
|
||||
|
||||
return jsonify({
|
||||
"errcode": "M_INVALID_PARAM",
|
||||
"error": "Expected UserID string to start with '@'"
|
||||
})
|
||||
|
||||
|
||||
@admin.route("/_matrix/client/unstable/uk.timedout.msc4323/admin/suspend/<user>", methods=["GET", "PUT"])
|
||||
@admin.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})
|
||||
|
||||
|
||||
@admin.route("/_matrix/client/unstable/uk.timedout.msc4323/admin/lock/<user>", methods=["GET", "PUT"])
|
||||
@admin.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})
|
||||
|
||||
|
||||
@admin.route("/_matrix/client/unstable/uk.timedout.msc4375/admin/rooms")
|
||||
@admin.route("/_matrix/client/v1/admin/rooms")
|
||||
async def rooms():
|
||||
return jsonify({
|
||||
"chunk": [
|
||||
f"!qa:{config.server_name}",
|
||||
f"!br:{config.server_name}",
|
||||
f"!3:{config.server_name}",
|
||||
f"!D:{config.server_name}",
|
||||
f"!U:{config.server_name}",
|
||||
f"!f:{config.server_name}",
|
||||
f"!gx:{config.server_name}",
|
||||
f"!hx:{config.server_name}",
|
||||
f"!iy:{config.server_name}",
|
||||
f"!p0:{config.server_name}",
|
||||
f"!jZ:{config.server_name}",
|
||||
]
|
||||
})
|
||||
|
||||
|
||||
|
||||
@admin.route("/_matrix/client/unstable/uk.timedout.msc4375/admin/rooms/<room>")
|
||||
@admin.route("/_matrix/client/v1/admin/rooms/<room>")
|
||||
async def room_state(room):
|
||||
if ":" not in room:
|
||||
return jsonify({
|
||||
"errcode": "M_NOT_FOUND",
|
||||
"error": "Unknown room"
|
||||
}), 404
|
||||
else:
|
||||
if room.split(":")[1] != config.server_name:
|
||||
return jsonify({
|
||||
"errcode": "M_NOT_FOUND",
|
||||
"error": "Unknown room"
|
||||
}), 404
|
||||
|
||||
state = globals.strip_state(
|
||||
send_join(bullshit, room)["state"]
|
||||
)
|
||||
|
||||
return jsonify({
|
||||
"state": [state]
|
||||
})
|
||||
|
||||
@admin.route("/_matrix/client/unstable/uk.timedout.msc4375/admin/rooms/<room>/evacuate", methods=["POST"])
|
||||
@admin.route("/_matrix/client/unstable/uk.timedout.msc4375/admin/rooms/<room>", methods=["DELETE"])
|
||||
@admin.route("/_matrix/client/v1/admin/rooms/<room>/evacuate", methods=["POST"])
|
||||
@admin.route("/_matrix/client/v1/admin/rooms/<room>", methods=["DELETE"])
|
||||
async def evacuate_room(room):
|
||||
req = request.json
|
||||
|
||||
if (
|
||||
isinstance(req, dict)
|
||||
and "background" in req
|
||||
and isinstance(req["background"], bool)
|
||||
):
|
||||
background = req["background"]
|
||||
else:
|
||||
background = True
|
||||
|
||||
resp = {
|
||||
"background": background,
|
||||
}
|
||||
|
||||
if not background:
|
||||
resp["removed"] = config.the_funny_number
|
||||
|
||||
return resp
|
||||
|
||||
|
||||
@admin.route("/_matrix/client/unstable/uk.timedout.msc4375/admin/rooms/<room>/evacuate/status")
|
||||
@admin.route("/_matrix/client/unstable/uk.timedout.msc4375/admin/rooms/<room>/delete/status")
|
||||
@admin.route("/_matrix/client/v1/admin/rooms/<room>/evacuate/status")
|
||||
@admin.route("/_matrix/client/v1/admin/rooms/<room>/delete/status")
|
||||
async def evacuate_status(room):
|
||||
return jsonify({
|
||||
"started_at": globals.get_time(), # everything all at once always now!!!
|
||||
"total": config.the_funny_number,
|
||||
"evacuated": config.the_funny_number,
|
||||
"failed": config.the_funny_number,
|
||||
})
|
||||
|
||||
|
||||
@admin.route("/_matrix/client/unstable/uk.timedout.msc4375/admin/rooms/<room>/blocked", methods=["PUT"])
|
||||
@admin.route("/_matrix/client/v1/admin/rooms/<room>/blocked", methods=["PUT"])
|
||||
async def block(room):
|
||||
return jsonify({})
|
||||
@@ -1,6 +1,5 @@
|
||||
import vona.globals as globals
|
||||
import vona.config as config
|
||||
import time
|
||||
|
||||
from flask import (
|
||||
Blueprint,
|
||||
@@ -89,7 +88,7 @@ async def users(group):
|
||||
"attestation": globals.sign_json({
|
||||
"group_id": group,
|
||||
"user_id": f"@vona:{config.server_name}",
|
||||
"valid_until_ms": int(str(time.time() * 1000).split(".")[0])
|
||||
"valid_until_ms": globals.get_time()
|
||||
})
|
||||
}
|
||||
],
|
||||
|
||||
@@ -36,6 +36,10 @@ def _load_toml(path: Path) -> dict:
|
||||
except tomllib.TOMLDecodeError as e:
|
||||
_fatal(f"Invalid TOML configuration: {e}")
|
||||
|
||||
# This will never be reached. It is here
|
||||
# specifically just to please ty
|
||||
return {}
|
||||
|
||||
|
||||
def _read_signing_key_from_path(path_value) -> str | None:
|
||||
p = Path(path_value)
|
||||
@@ -111,11 +115,11 @@ def _apply_config(cfg: dict) -> None:
|
||||
else:
|
||||
_warn("No support contacts are defined")
|
||||
|
||||
if "enable_registration" in cfg:
|
||||
users_can_register = cfg["enable_registration"]
|
||||
|
||||
support = support_obj
|
||||
|
||||
if "enable_registration" in cfg and isinstance(cfg["enable_registration"], bool):
|
||||
users_can_register = cfg["enable_registration"]
|
||||
|
||||
print("[INFO] Configuration file was valid")
|
||||
|
||||
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
import vona.config
|
||||
str(vona.config.server_name) # Satisfy Ruff
|
||||
|
||||
@@ -2,13 +2,6 @@ from flask import (
|
||||
Blueprint,
|
||||
)
|
||||
|
||||
custom = Blueprint("custom", __name__)
|
||||
|
||||
# This implements non-standard
|
||||
# endpoints created by other
|
||||
# homeserver implementations.
|
||||
|
||||
|
||||
from .hammerhead import hammerhead
|
||||
from .conduwuit import conduwuit
|
||||
from .dendrite import dendrite
|
||||
@@ -16,6 +9,13 @@ from .telodendria import telo
|
||||
from .synapse import synapse
|
||||
from .citadel import citadel
|
||||
|
||||
custom = Blueprint("custom", __name__)
|
||||
|
||||
# This implements non-standard
|
||||
# endpoints created by other
|
||||
# homeserver implementations.
|
||||
|
||||
|
||||
custom.register_blueprint(hammerhead)
|
||||
custom.register_blueprint(conduwuit)
|
||||
custom.register_blueprint(dendrite)
|
||||
|
||||
@@ -30,7 +30,16 @@ async def news_stats(event):
|
||||
|
||||
@citadel.route("/_matrix/client/r0/citadel/rooms/<room>/closeRoom", methods=["POST"])
|
||||
async def close_room(room):
|
||||
store_response = request.json.get("store_response", True)
|
||||
req = request.json
|
||||
|
||||
if (
|
||||
isinstance(req, dict)
|
||||
and "store_response" in req
|
||||
and isinstance(req["store_response"], bool)
|
||||
):
|
||||
store_response = req["store_response"]
|
||||
else:
|
||||
store_response = True
|
||||
|
||||
operation_id = base64.b64encode(
|
||||
bytes(
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
from datetime import datetime, timezone
|
||||
from vona.federation import send_join
|
||||
import vona.globals as globals
|
||||
import vona.config as config
|
||||
import time
|
||||
@@ -9,6 +8,11 @@ from flask import (
|
||||
jsonify,
|
||||
)
|
||||
|
||||
from vona.federation import (
|
||||
send_join,
|
||||
bullshit,
|
||||
)
|
||||
|
||||
hammerhead = Blueprint("hammerhead", __name__)
|
||||
|
||||
# Hammerhead endpoints. Not documented, but code is at:
|
||||
@@ -24,6 +28,7 @@ async def uptime():
|
||||
async def version():
|
||||
return jsonify({"version": globals.version})
|
||||
|
||||
|
||||
@hammerhead.route("/_hammerhead/admin/reload-config")
|
||||
async def reload_config():
|
||||
return jsonify({})
|
||||
@@ -61,10 +66,6 @@ async def server_info(server):
|
||||
|
||||
@hammerhead.route("/_hammerhead/admin/rooms/<room>/state-resolver")
|
||||
async def room_state(room):
|
||||
class bullshit:
|
||||
def get_json():
|
||||
return {}
|
||||
|
||||
state = send_join(bullshit, room)["state"]
|
||||
formatted_state = {}
|
||||
event_cache = {}
|
||||
@@ -90,3 +91,44 @@ async def room_state(room):
|
||||
"room_id": room,
|
||||
"room_version": globals.room_version_from_id(room)
|
||||
})
|
||||
|
||||
|
||||
@hammerhead.route("/_hammerhead/admin/rooms/<room>/debug")
|
||||
async def debug_room(room):
|
||||
state = send_join(bullshit, room)["state"]
|
||||
formatted_state = {}
|
||||
event_cache = {}
|
||||
|
||||
for event in state:
|
||||
key = f"({event["type"]},'{event["state_key"]}')"
|
||||
formatted_state[key] = event
|
||||
|
||||
if "event_id" in event:
|
||||
event_id = event["event_id"]
|
||||
else:
|
||||
event_id = globals.make_ref_hash(
|
||||
event,
|
||||
int(globals.room_version_from_id(room))
|
||||
)
|
||||
|
||||
event_cache[event_id] = event
|
||||
|
||||
|
||||
if globals.room_version_from_id(room) in ["1", "2"]:
|
||||
extremity = globals.make_event_id(seed=f"6_{room}")
|
||||
else:
|
||||
extremity = globals.make_ref_hash(
|
||||
state[5],
|
||||
int(globals.room_version_from_id(room)),
|
||||
)
|
||||
|
||||
|
||||
return jsonify({
|
||||
"aliases": [f"#vona:{config.server_name}"],
|
||||
"current_state": formatted_state,
|
||||
"event_cache": event_cache,
|
||||
"room_id": room,
|
||||
"room_version": globals.room_version_from_id(room),
|
||||
"forward_extremities": [extremity],
|
||||
"servers": None
|
||||
})
|
||||
|
||||
@@ -32,6 +32,7 @@ synapse = Blueprint("synapse", __name__)
|
||||
@synapse.route("/_synapse/admin/v1/rooms/<room>/timestamp_to_event")
|
||||
@synapse.route("/_synapse/admin/v2/rooms/delete_status/<delete_id>")
|
||||
@synapse.route("/_synapse/admin/v1/rooms/<room>/make_room_admin", methods=["POST"])
|
||||
@synapse.route("/_synapse/admin/v1/background_updates/start_job", methods=["POST"])
|
||||
async def response(**kwargs):
|
||||
return jsonify({})
|
||||
|
||||
@@ -275,13 +276,7 @@ async def room_stats():
|
||||
@synapse.route("/_synapse/admin/v1/background_updates/enabled", methods=["POST", "GET"])
|
||||
@synapse.route("/_synapse/admin/v1/background_updates/status")
|
||||
async def change_bg_update():
|
||||
return jsonify({"enabled":False})
|
||||
|
||||
# No documentation on what Synapse actually returns for this API, so a blank dict for now
|
||||
@synapse.route("/_synapse/admin/v1/background_updates/start_job", methods=["POST"])
|
||||
async def bg_update_start_job():
|
||||
return jsonify({})
|
||||
|
||||
return jsonify({"enabled": False})
|
||||
|
||||
@synapse.route("/_synapse/admin/v1/event_reports")
|
||||
async def event_reports():
|
||||
|
||||
@@ -10,6 +10,7 @@ from flask import (
|
||||
Response,
|
||||
request,
|
||||
Blueprint,
|
||||
abort,
|
||||
)
|
||||
|
||||
server = Blueprint("federation", __name__)
|
||||
@@ -37,6 +38,7 @@ async def version():
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@server.route("/_matrix/key/v2/server")
|
||||
async def keys():
|
||||
return jsonify(globals.sign_json({
|
||||
@@ -50,6 +52,7 @@ async def keys():
|
||||
}
|
||||
}))
|
||||
|
||||
|
||||
@server.route("/_matrix/federation/v1/query/directory")
|
||||
async def room_query():
|
||||
return jsonify({
|
||||
@@ -57,6 +60,7 @@ async def room_query():
|
||||
"servers": [config.server_name]
|
||||
})
|
||||
|
||||
|
||||
@server.route("/_matrix/federation/v1/media/download/<media_id>")
|
||||
async def download_media(media_id):
|
||||
# Auth media requires this to be
|
||||
@@ -80,6 +84,7 @@ async def download_media(media_id):
|
||||
|
||||
return response
|
||||
|
||||
|
||||
@server.route("/_matrix/federation/v1/media/thumbnail/<media_id>")
|
||||
async def thumbnail_media(media_id):
|
||||
return jsonify({
|
||||
@@ -88,7 +93,7 @@ async def thumbnail_media(media_id):
|
||||
}), 418
|
||||
|
||||
|
||||
@server.route("/_matrix/federation/v1/send_join/<room>/<eventId>", methods=["PUT"])
|
||||
@server.route("/_matrix/federation/v1/send_join/<room>/<path:eventId>", methods=["PUT"])
|
||||
async def send_join_v1(room, eventId):
|
||||
if globals.room_version_from_id(room) not in ["1", "2"]:
|
||||
return jsonify({
|
||||
@@ -99,8 +104,12 @@ async def send_join_v1(room, eventId):
|
||||
return jsonify([200, send_join(request, room)])
|
||||
|
||||
|
||||
@server.route("/_matrix/federation/v2/send_join/<room>/<eventId>", methods=["PUT"])
|
||||
@server.route("/_matrix/federation/v2/send_join/<room>/<path:eventId>", methods=["PUT"])
|
||||
async def send_join_v2(room, eventId):
|
||||
if globals.room_version_from_id(room) in ["1", "2"]:
|
||||
# Spec says to fallback to the v1 send_join endpoint if this fails
|
||||
abort(404)
|
||||
|
||||
return jsonify(send_join(request, room))
|
||||
|
||||
|
||||
@@ -128,7 +137,6 @@ async def make_join(room, user):
|
||||
|
||||
join = {
|
||||
"content": {
|
||||
"join_authorised_via_users_server": f"@vona:{config.server_name}",
|
||||
"membership": "join"
|
||||
},
|
||||
"origin": config.server_name,
|
||||
@@ -175,7 +183,7 @@ async def make_join(room, user):
|
||||
|
||||
|
||||
return jsonify({
|
||||
"event": globals.hash_and_sign_event(join),
|
||||
"event": globals.hash_and_sign_event(join, int(room_ver)),
|
||||
"room_version": room_ver
|
||||
})
|
||||
|
||||
@@ -185,7 +193,6 @@ async def room_directory():
|
||||
return jsonify(globals.room_dir)
|
||||
|
||||
|
||||
# https://spec.matrix.org/latest/server-server-api/#transactions
|
||||
@server.route("/_matrix/federation/v1/send/<txnId>", methods=["PUT"])
|
||||
async def receive_txn(txnId):
|
||||
# We will need to implement a way to store every
|
||||
@@ -209,7 +216,13 @@ async def receive_txn(txnId):
|
||||
if "pdus" in parsed_data:
|
||||
for pdu in parsed_data["pdus"]:
|
||||
if "event_id" in pdu:
|
||||
response["pdus"][pdu["event_id"]] = {}
|
||||
# For v1 and v2 rooms
|
||||
event_id = pdu["event_id"]
|
||||
else:
|
||||
# Assume room v4 or over as most rooms will be anyway
|
||||
event_id = globals.make_ref_hash(pdu, 4)
|
||||
|
||||
response["pdus"][event_id] = {}
|
||||
|
||||
return jsonify(response)
|
||||
|
||||
@@ -243,8 +256,19 @@ async def user_devices(user):
|
||||
|
||||
@server.route("/_matrix/federation/v1/user/keys/query", methods=["POST"])
|
||||
async def user_keys():
|
||||
req = request.json
|
||||
|
||||
if (
|
||||
isinstance(req, dict)
|
||||
and "device_keys" in req
|
||||
and isinstance(req["device_keys"], dict)
|
||||
):
|
||||
device_keys = req["device_keys"]
|
||||
else:
|
||||
device_keys = {}
|
||||
|
||||
return jsonify({
|
||||
"device_keys": request.json.get("device_keys", {})
|
||||
"device_keys": device_keys
|
||||
})
|
||||
|
||||
|
||||
@@ -252,9 +276,12 @@ async def user_keys():
|
||||
async def invite_user_v2(room, txnId):
|
||||
invite_data = request.json
|
||||
|
||||
if "event" in invite_data:
|
||||
if (
|
||||
isinstance(invite_data, dict)
|
||||
and "event" in invite_data
|
||||
):
|
||||
if "room_version" in invite_data:
|
||||
if invite_data["room_version"] not in ["1", "2"]:
|
||||
if invite_data["room_version"] not in [str(i) for i in range(1, 10)]:
|
||||
return jsonify({
|
||||
"errcode": "M_INCOMPATIBLE_ROOM_VERSION",
|
||||
"error": "Unsupported room version",
|
||||
@@ -287,10 +314,19 @@ async def invite_user_v2(room, txnId):
|
||||
@server.route("/_matrix/federation/v1/invite/<room>/<txnId>", methods=["PUT"])
|
||||
async def invite_user_v1(room, txnId):
|
||||
event = request.json
|
||||
content = event.get("content", {})
|
||||
if (
|
||||
isinstance(event, dict)
|
||||
and "content" in event
|
||||
and isinstance(event["content"], dict)
|
||||
):
|
||||
content = event["content"]
|
||||
else:
|
||||
content = {}
|
||||
|
||||
if (
|
||||
"content" in event
|
||||
isinstance(event, dict)
|
||||
and isinstance(content, dict)
|
||||
and "content" in event
|
||||
and "membership" in content
|
||||
and "state_key" in event
|
||||
and "room_id" in event
|
||||
@@ -320,7 +356,12 @@ async def space_hierachy(room):
|
||||
@server.route("/_matrix/federation/v1/org.matrix.msc4358/discover_common_rooms", methods=["POST"])
|
||||
@server.route("/_matrix/federation/v1/discover_common_rooms", methods=["POST"])
|
||||
async def discover_common_rooms():
|
||||
tags = request.json.get("room_participation_tags", [])
|
||||
req = request.json
|
||||
if isinstance(req, dict):
|
||||
tags = req.get("room_participation_tags", [])
|
||||
else:
|
||||
tags = []
|
||||
|
||||
return jsonify({"recognised_tags": tags})
|
||||
|
||||
|
||||
@@ -330,7 +371,7 @@ async def backfill(room):
|
||||
|
||||
return jsonify({
|
||||
"origin": config.server_name,
|
||||
"origin_server_ts": int(str(time.time() * 1000).split(".")[0]),
|
||||
"origin_server_ts": globals.get_time(),
|
||||
"pdus": send_join(bullshit, room)["state"]
|
||||
})
|
||||
|
||||
@@ -342,7 +383,7 @@ async def extremities(room):
|
||||
if room.split(":")[1] != config.server_name:
|
||||
return jsonify({
|
||||
"errcode": "M_NOT_FOUND",
|
||||
"error": f"Room is unknown to this server"
|
||||
"error": "Room is unknown to this server"
|
||||
}), 404
|
||||
|
||||
room_ver = globals.room_version_from_id(room)
|
||||
@@ -414,7 +455,7 @@ async def state_ids(room):
|
||||
raise
|
||||
|
||||
return jsonify(resp.json())
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return explode()
|
||||
|
||||
@@ -199,7 +199,7 @@ def v1_v2(request, room) -> dict:
|
||||
|
||||
remote_join = request.get_json()
|
||||
|
||||
response = {
|
||||
return {
|
||||
"auth_chain": event_chain,
|
||||
"event": remote_join,
|
||||
"members_omitted": False,
|
||||
@@ -207,10 +207,8 @@ def v1_v2(request, room) -> dict:
|
||||
"state": event_chain
|
||||
}
|
||||
|
||||
return response
|
||||
|
||||
|
||||
# Room V3 to V9
|
||||
# Room V3 to V10
|
||||
def v3(request, room) -> dict:
|
||||
initial_response = v1_v2(request, room)
|
||||
state = list(initial_response["state"])
|
||||
@@ -225,12 +223,14 @@ def v3(request, room) -> dict:
|
||||
del event["signatures"]
|
||||
|
||||
# m.room.create doesn't have prev_events or auth_events
|
||||
events["m.room.create"] = globals.hash_and_sign_event(events["m.room.create"])
|
||||
if ver >= 11:
|
||||
del events["m.room.create"]["content"]["creator"]
|
||||
events["m.room.create"] = globals.hash_and_sign_event(events["m.room.create"], ver)
|
||||
hash_map["m.room.create"] = globals.make_ref_hash(events["m.room.create"], ver)
|
||||
|
||||
events["m.room.member"]["auth_events"] = [hash_map["m.room.create"]]
|
||||
events["m.room.member"]["prev_events"] = [hash_map["m.room.create"]]
|
||||
events["m.room.member"] = globals.hash_and_sign_event(events["m.room.member"])
|
||||
events["m.room.member"] = globals.hash_and_sign_event(events["m.room.member"], ver)
|
||||
hash_map["m.room.member"] = globals.make_ref_hash(events["m.room.member"], ver)
|
||||
|
||||
events["m.room.power_levels"]["auth_events"] = [
|
||||
@@ -238,7 +238,9 @@ def v3(request, room) -> dict:
|
||||
hash_map["m.room.member"],
|
||||
]
|
||||
events["m.room.power_levels"]["prev_events"] = [hash_map["m.room.member"]]
|
||||
events["m.room.power_levels"] = globals.hash_and_sign_event(events["m.room.power_levels"])
|
||||
if ver >= 10:
|
||||
events["m.room.power_levels"]["content"]["users"][f"@vona:{config.server_name}"] = 100
|
||||
events["m.room.power_levels"] = globals.hash_and_sign_event(events["m.room.power_levels"], ver)
|
||||
hash_map["m.room.power_levels"] = globals.make_ref_hash(events["m.room.power_levels"], ver)
|
||||
|
||||
events["m.room.join_rules"]["auth_events"] = [
|
||||
@@ -247,7 +249,7 @@ def v3(request, room) -> dict:
|
||||
hash_map["m.room.power_levels"],
|
||||
]
|
||||
events["m.room.join_rules"]["prev_events"] = [hash_map["m.room.power_levels"]]
|
||||
events["m.room.join_rules"] = globals.hash_and_sign_event(events["m.room.join_rules"])
|
||||
events["m.room.join_rules"] = globals.hash_and_sign_event(events["m.room.join_rules"], ver)
|
||||
hash_map["m.room.join_rules"] = globals.make_ref_hash(events["m.room.join_rules"], ver)
|
||||
|
||||
events["m.room.guest_access"]["auth_events"] = [
|
||||
@@ -256,7 +258,7 @@ def v3(request, room) -> dict:
|
||||
hash_map["m.room.power_levels"],
|
||||
]
|
||||
events["m.room.guest_access"]["prev_events"] = [hash_map["m.room.join_rules"]]
|
||||
events["m.room.guest_access"] = globals.hash_and_sign_event(events["m.room.guest_access"])
|
||||
events["m.room.guest_access"] = globals.hash_and_sign_event(events["m.room.guest_access"], ver)
|
||||
hash_map["m.room.guest_access"] = globals.make_ref_hash(events["m.room.guest_access"], ver)
|
||||
|
||||
events["m.room.history_visibility"]["auth_events"] = [
|
||||
@@ -265,7 +267,7 @@ def v3(request, room) -> dict:
|
||||
hash_map["m.room.power_levels"],
|
||||
]
|
||||
events["m.room.history_visibility"]["prev_events"] = [hash_map["m.room.guest_access"]]
|
||||
events["m.room.history_visibility"] = globals.hash_and_sign_event(events["m.room.history_visibility"])
|
||||
events["m.room.history_visibility"] = globals.hash_and_sign_event(events["m.room.history_visibility"], ver)
|
||||
|
||||
new_state = []
|
||||
|
||||
@@ -273,12 +275,10 @@ def v3(request, room) -> dict:
|
||||
new_state.append(events[event])
|
||||
|
||||
|
||||
resp = {
|
||||
return {
|
||||
"auth_chain": new_state,
|
||||
"event": initial_response["event"],
|
||||
"members_omitted": False,
|
||||
"servers_in_room": [config.server_name],
|
||||
"state": new_state
|
||||
}
|
||||
|
||||
return resp
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
from resolvematrix import ServerResolver
|
||||
from types import SimpleNamespace
|
||||
from collections import Counter
|
||||
|
||||
import vona.config as config
|
||||
import nacl.encoding
|
||||
import nacl.signing
|
||||
import hashlib
|
||||
import base64
|
||||
import random
|
||||
import httpx
|
||||
import json
|
||||
import copy
|
||||
import time
|
||||
import re
|
||||
|
||||
version = "1.5.0"
|
||||
|
||||
|
||||
def canonical_json(value):
|
||||
def canonical_json(value: dict | list) -> bytes:
|
||||
return json.dumps(
|
||||
value,
|
||||
ensure_ascii=False,
|
||||
@@ -22,7 +26,7 @@ def canonical_json(value):
|
||||
).encode("UTF-8")
|
||||
|
||||
|
||||
def sign_json(data):
|
||||
def sign_json(data: dict) -> dict:
|
||||
parts = config.signing_key.split()
|
||||
base64_key = parts[2]
|
||||
|
||||
@@ -51,7 +55,7 @@ def sign_json(data):
|
||||
return signed_json
|
||||
|
||||
|
||||
def sign_json_without_discard(data):
|
||||
def sign_json_without_discard(data: dict) -> dict:
|
||||
parts = config.signing_key.split()
|
||||
base64_key = parts[2]
|
||||
|
||||
@@ -84,7 +88,7 @@ def sign_json_without_discard(data):
|
||||
return data
|
||||
|
||||
|
||||
def make_event_id(seed=None):
|
||||
def make_event_id(seed: str | int | None = None) -> str:
|
||||
if seed is not None:
|
||||
random.seed(seed)
|
||||
|
||||
@@ -102,7 +106,7 @@ def make_event_id(seed=None):
|
||||
return event_id
|
||||
|
||||
|
||||
def event_hash(event_object):
|
||||
def event_hash(event_object: dict) -> str:
|
||||
event_object = dict(event_object)
|
||||
|
||||
event_object.pop("unsigned", None)
|
||||
@@ -171,9 +175,10 @@ def make_auth_header(
|
||||
def redact_event(
|
||||
event: dict,
|
||||
for_event_id: bool = False,
|
||||
):
|
||||
room_ver: int = 1,
|
||||
) -> dict:
|
||||
# Returns a redacted event as per
|
||||
# the algorithm for v1/v2 rooms.
|
||||
# the algorithm for v1 to v11 rooms.
|
||||
|
||||
allowed_keys = [
|
||||
"event_id",
|
||||
@@ -185,16 +190,18 @@ def redact_event(
|
||||
"hashes",
|
||||
"depth",
|
||||
"prev_events",
|
||||
"prev_state",
|
||||
"auth_events",
|
||||
"origin",
|
||||
"origin_server_ts",
|
||||
"membership",
|
||||
]
|
||||
|
||||
if not for_event_id:
|
||||
allowed_keys.append("signatures")
|
||||
|
||||
if room_ver < 11:
|
||||
allowed_keys.append("origin")
|
||||
allowed_keys.append("membership")
|
||||
allowed_keys.append("prev_state")
|
||||
|
||||
redacted_event = {k: v for k, v in event.items() if k in allowed_keys}
|
||||
|
||||
if "type" in redacted_event and "content" in redacted_event:
|
||||
@@ -218,23 +225,63 @@ def redact_event(
|
||||
"m.room.history_visibility": ["history_visibility"],
|
||||
}
|
||||
|
||||
if room_ver >= 6:
|
||||
del content_key_rules["m.room.aliases"]
|
||||
|
||||
if room_ver >= 8:
|
||||
content_key_rules["m.room.join_rules"].append("allow")
|
||||
|
||||
if room_ver >= 9:
|
||||
content_key_rules["m.room.member"].append("join_authorised_via_users_server")
|
||||
|
||||
if room_ver >= 11:
|
||||
content_key_rules["m.room.redaction"] = ["redacts"]
|
||||
del content_key_rules["m.room.create"] # All keys will be permitted
|
||||
|
||||
|
||||
if event_type in content_key_rules:
|
||||
allowed_content_keys = content_key_rules[event_type]
|
||||
|
||||
if (
|
||||
room_ver >= 11
|
||||
and "third_party_invite" in redacted_event
|
||||
and "signed" in redacted_event["third_party_invite"]
|
||||
):
|
||||
third_party_invite_signature = copy.deepcopy(redacted_event["third_party_invite"]["signed"])
|
||||
else:
|
||||
third_party_invite_signature = None
|
||||
|
||||
redacted_event["content"] = {
|
||||
k: v
|
||||
for k, v in redacted_event["content"].items()
|
||||
if k in allowed_content_keys
|
||||
}
|
||||
|
||||
if third_party_invite_signature:
|
||||
redacted_event["content"]["third_party_invite"] = {
|
||||
"signed": third_party_invite_signature
|
||||
}
|
||||
|
||||
else:
|
||||
if room_ver >= 11 and event_type == "m.room.create":
|
||||
pass
|
||||
else:
|
||||
redacted_event["content"] = {}
|
||||
|
||||
return redacted_event
|
||||
|
||||
|
||||
def hash_and_sign_event(event_object):
|
||||
def hash_and_sign_event(
|
||||
event_object: dict,
|
||||
room_ver: int = 1,
|
||||
) -> dict:
|
||||
content_hash = event_hash(event_object)
|
||||
event_object["hashes"] = {"sha256": content_hash}
|
||||
stripped_object = redact_event(event_object)
|
||||
stripped_object = redact_event(
|
||||
event=event_object,
|
||||
for_event_id=False,
|
||||
room_ver=room_ver,
|
||||
)
|
||||
signed_object = sign_json(stripped_object)
|
||||
event_object["signatures"] = signed_object["signatures"]
|
||||
return event_object
|
||||
@@ -243,8 +290,12 @@ def hash_and_sign_event(event_object):
|
||||
def make_ref_hash(
|
||||
event: dict,
|
||||
room_ver: int = 3,
|
||||
):
|
||||
stripped = redact_event(event, True)
|
||||
) -> str:
|
||||
stripped = redact_event(
|
||||
event=event,
|
||||
for_event_id=True,
|
||||
room_ver=room_ver,
|
||||
)
|
||||
evt_bytes = canonical_json(stripped)
|
||||
|
||||
evt_hash = base64.b64encode(
|
||||
@@ -260,7 +311,7 @@ def make_ref_hash(
|
||||
return "$" + evt_hash
|
||||
|
||||
|
||||
def room_version_from_id(room):
|
||||
def room_version_from_id(room) -> str:
|
||||
room_id_no_sigil = (
|
||||
room
|
||||
.replace("!", "")
|
||||
@@ -268,7 +319,8 @@ def room_version_from_id(room):
|
||||
)
|
||||
hexadecimal_room_id = bytes(room_id_no_sigil, "utf-8").hex()
|
||||
|
||||
versions = [str(i) for i in range(1, 10)]
|
||||
versions = [str(i) for i in range(0, 10)]
|
||||
versions.append("a")
|
||||
|
||||
if not any(ver in hexadecimal_room_id for ver in versions):
|
||||
hexadecimal_room_id = "2" + hexadecimal_room_id[1:]
|
||||
@@ -282,7 +334,13 @@ def room_version_from_id(room):
|
||||
s = s.replace(" ", "").lower()
|
||||
counts = Counter(s)
|
||||
most_common = counts.most_common(1)
|
||||
return most_common[0] if most_common else ("2",)
|
||||
|
||||
actual_most_common = most_common[0] if most_common else ("4",)
|
||||
if actual_most_common[0] == "0":
|
||||
return ("10",)
|
||||
elif actual_most_common[0] == "a":
|
||||
return ("11",)
|
||||
return actual_most_common
|
||||
|
||||
return str(most_common_character(nums)[0])
|
||||
|
||||
@@ -374,9 +432,9 @@ class http_client:
|
||||
)
|
||||
|
||||
|
||||
def strip_state(l) -> list:
|
||||
if not isinstance(l, list):
|
||||
return l
|
||||
def strip_state(state_events) -> list:
|
||||
if not isinstance(state_events, list):
|
||||
return state_events
|
||||
|
||||
keys_to_remove = [
|
||||
"auth_events",
|
||||
@@ -387,15 +445,26 @@ def strip_state(l) -> list:
|
||||
]
|
||||
|
||||
new_list = []
|
||||
for d in l:
|
||||
for d in state_events:
|
||||
if not isinstance(d, dict):
|
||||
new_list.append(d)
|
||||
continue
|
||||
|
||||
if "room_id" in d:
|
||||
ver = int(room_version_from_id(d["room_id"]))
|
||||
else:
|
||||
ver = 4
|
||||
|
||||
event_id = make_ref_hash(d, ver)
|
||||
new_dict = {}
|
||||
for k, v in d.items():
|
||||
if k in keys_to_remove:
|
||||
continue
|
||||
new_dict[k] = strip_state(v)
|
||||
new_dict["event_id"] = event_id
|
||||
new_list.append(new_dict)
|
||||
return new_list
|
||||
|
||||
|
||||
def get_time() -> int:
|
||||
return int(str(time.time() * 1000).split(".")[0])
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
from vona.config import server_name, the_funny_number
|
||||
import vona.config as config
|
||||
import vona.globals as globals
|
||||
import time
|
||||
import os
|
||||
|
||||
from flask import (
|
||||
Blueprint,
|
||||
@@ -13,10 +15,8 @@ identity = Blueprint("identity", __name__)
|
||||
# I'm pretty sure only Element uses this,
|
||||
# but oh well.
|
||||
|
||||
# https://spec.matrix.org/latest/identity-service-api/#api-version-check
|
||||
@identity.route("/_matrix/identity/versions")
|
||||
async def versions():
|
||||
# Stolen from the vector.im identity server
|
||||
return jsonify({
|
||||
"versions": [
|
||||
"r0.1.0",
|
||||
@@ -32,10 +32,11 @@ async def versions():
|
||||
})
|
||||
|
||||
|
||||
# https://spec.matrix.org/latest/identity-service-api/#authentication
|
||||
@identity.route("/_matrix/identity/v2/account")
|
||||
async def account_info():
|
||||
return jsonify({"user_id": f"@vona:{server_name}"})
|
||||
return jsonify({
|
||||
"user_id": f"@vona:{config.server_name}"
|
||||
})
|
||||
|
||||
|
||||
@identity.route("/_matrix/identity/v2/account/logout", methods=["POST"])
|
||||
@@ -45,98 +46,129 @@ async def logout():
|
||||
|
||||
@identity.route("/_matrix/identity/v2/account/register", methods=["POST"])
|
||||
async def register():
|
||||
return jsonify({"token":"vona"})
|
||||
return jsonify({"token": "vona"})
|
||||
|
||||
|
||||
# https://spec.matrix.org/latest/identity-service-api/#terms-of-service
|
||||
@identity.route("/_matrix/identity/v2/terms", methods=["GET", "POST"])
|
||||
async def policies():
|
||||
if request.method == "GET":
|
||||
return jsonify({"policies":{}})
|
||||
return jsonify({
|
||||
"policies": {}
|
||||
})
|
||||
|
||||
return jsonify({})
|
||||
|
||||
|
||||
@identity.route("/_matrix/identity/v2")
|
||||
async def status():
|
||||
return jsonify({})
|
||||
|
||||
|
||||
@identity.route("/_matrix/identity/v2/pubkey/ephemeral/isvalid")
|
||||
@identity.route("/_matrix/identity/v2/pubkey/isvalid")
|
||||
async def pubkey_validity():
|
||||
return jsonify({"valid": True})
|
||||
|
||||
|
||||
@identity.route("/_matrix/identity/v2/pubkey/<key>")
|
||||
async def get_key(key):
|
||||
return jsonify({
|
||||
"errcode": "M_NOT_FOUND",
|
||||
"error": "The public key was not found"
|
||||
}), 404
|
||||
"public_key": globals.pubkey()
|
||||
})
|
||||
|
||||
|
||||
@identity.route("/_matrix/identity/v2/hash_details")
|
||||
async def hash_details():
|
||||
return jsonify({"algorithms":["none","sha256"],"lookup_pepper": "vona"})
|
||||
return jsonify({
|
||||
"algorithms": [
|
||||
"none",
|
||||
"sha256",
|
||||
],
|
||||
"lookup_pepper": "vona"
|
||||
})
|
||||
|
||||
|
||||
@identity.route("/_matrix/identity/v2/lookup", methods=["POST"])
|
||||
async def lookup():
|
||||
req = request.json
|
||||
|
||||
if "addresses" in req:
|
||||
return jsonify({"mappings": {req["addresses"][0]: f"@vona:{server_name}"}})
|
||||
else:
|
||||
return jsonify({"errcode": "M_INVALID_PEPPER","error": "Invalid pepper"})
|
||||
if (
|
||||
isinstance(req, dict)
|
||||
and "addresses" in req
|
||||
and isinstance(req["addresses"], list)
|
||||
and len(req["addresses"]) > 0
|
||||
):
|
||||
return jsonify({
|
||||
"mappings": {
|
||||
req["addresses"][0]: f"@vona:{config.server_name}"
|
||||
}
|
||||
})
|
||||
|
||||
return jsonify({
|
||||
"errcode": "M_INVALID_PEPPER",
|
||||
"error": "Invalid pepper"
|
||||
})
|
||||
|
||||
|
||||
@identity.route("/_matrix/identity/v2/validate/email/requestToken", methods=["POST"])
|
||||
@identity.route("/_matrix/identity/v2/validate/msisdn/requestToken", methods=["POST"])
|
||||
async def request_validation_token():
|
||||
return jsonify({"sid": str(the_funny_number)})
|
||||
return jsonify({
|
||||
"sid": os.urandom(16).hex()
|
||||
})
|
||||
|
||||
|
||||
@identity.route("/_matrix/identity/v2/validate/email/submitToken", methods=["GET", "POST"])
|
||||
@identity.route("/_matrix/identity/v2/validate/msisdn/submitToken", methods=["GET", "POST"])
|
||||
async def submit_validation_token():
|
||||
return jsonify({"success": True})
|
||||
|
||||
|
||||
@identity.route("/_matrix/identity/v2/3pid/bind", methods=["POST"])
|
||||
async def threepid_bind():
|
||||
if "mxid" in request.get_json():
|
||||
mxid = request.get_json()["mxid"]
|
||||
else:
|
||||
mxid = f"@vona:{server_name}"
|
||||
mxid = f"@vona:{config.server_name}"
|
||||
|
||||
return jsonify(globals.sign_json({
|
||||
return jsonify(
|
||||
globals.sign_json({
|
||||
"address": "abuse@matrix.org",
|
||||
"medium": "email",
|
||||
"mxid": mxid,
|
||||
"not_after": int(time.time() * 1000 + 604800000),
|
||||
"not_before": int(time.time() * 1000 - 604800000),
|
||||
"ts": int(time.time() * 1000)
|
||||
}))
|
||||
})
|
||||
)
|
||||
|
||||
|
||||
@identity.route("/_matrix/identity/v2/3pid/unbind", methods=["POST"])
|
||||
async def threepid_unbind():
|
||||
return jsonify({})
|
||||
|
||||
|
||||
@identity.route("/_matrix/identity/v2/3pid/getValidated3pid")
|
||||
async def threepid_validated():
|
||||
# Please email abuse@matrix.org
|
||||
return jsonify({
|
||||
"address": "abuse@matrix.org",
|
||||
"medium": "email",
|
||||
"validated_at": the_funny_number
|
||||
"validated_at": config.the_funny_number
|
||||
})
|
||||
|
||||
|
||||
# https://spec.matrix.org/latest/identity-service-api/#invitation-storage
|
||||
@identity.route("/_matrix/identity/v2/store-invite", methods=["POST"])
|
||||
async def invite():
|
||||
return jsonify({
|
||||
"display_name": "Vona",
|
||||
"public_keys": [
|
||||
{
|
||||
"key_validity_url": f"https://{server_name}/_matrix/identity/v2/pubkey/isvalid",
|
||||
"key_validity_url": f"https://{config.server_name}/_matrix/identity/v2/pubkey/isvalid",
|
||||
"public_key": "ohyeah"
|
||||
},
|
||||
{
|
||||
"key_validity_url": f"https://{server_name}/_matrix/identity/v2/pubkey/ephemeral/isvalid",
|
||||
"key_validity_url": f"https://{config.server_name}/_matrix/identity/v2/pubkey/ephemeral/isvalid",
|
||||
"public_key": "burgerkingfootlettuce"
|
||||
}
|
||||
],
|
||||
@@ -144,7 +176,6 @@ async def invite():
|
||||
})
|
||||
|
||||
|
||||
# https://spec.matrix.org/latest/identity-service-api/#ephemeral-invitation-signing
|
||||
@identity.route("/_matrix/identity/v2/sign-ed25519", methods=["POST"])
|
||||
async def invite_signing():
|
||||
required_keys = {"mxid", "private_key", "token"}
|
||||
|
||||
@@ -98,7 +98,10 @@ try:
|
||||
except ValueError:
|
||||
join_event["origin_server_ts"] = int(f"{time.time() * 1000}".split(".")[0])
|
||||
|
||||
signed_join = globals.hash_and_sign_event(join_event)
|
||||
signed_join = globals.hash_and_sign_event(
|
||||
join_event,
|
||||
int(make_join.get("room_version", "1"))
|
||||
)
|
||||
|
||||
try:
|
||||
send_join_response = http_client.put(
|
||||
|
||||
@@ -1,21 +1,62 @@
|
||||
import vona.globals as globals
|
||||
import vona.config as config
|
||||
import os
|
||||
import multiprocessing
|
||||
|
||||
versions = [str(i) for i in range(1, 10)]
|
||||
desired_ver = input("Desired room version:\n\t")
|
||||
|
||||
if desired_ver not in versions:
|
||||
os._exit(1)
|
||||
|
||||
try:
|
||||
while True:
|
||||
room_id = os.urandom(8).hex()
|
||||
desired_ver = input("Desired room version:\n\t")
|
||||
except (EOFError, KeyboardInterrupt):
|
||||
print("")
|
||||
exit(0)
|
||||
|
||||
known = {
|
||||
"1": "qa",
|
||||
"2": "br",
|
||||
"3": "3",
|
||||
"4": "D",
|
||||
"5": "U",
|
||||
"6": "f",
|
||||
"7": "gx",
|
||||
"8": "hx",
|
||||
"9": "iy",
|
||||
"10": "p0",
|
||||
"11": "jZ",
|
||||
}
|
||||
|
||||
if desired_ver in known:
|
||||
print(f"!{known[desired_ver]}:{config.server_name} (from known room ID list)")
|
||||
try:
|
||||
input("Press enter to bruteforce anyway:\n\t")
|
||||
except (EOFError, KeyboardInterrupt):
|
||||
print("")
|
||||
exit(0)
|
||||
|
||||
elif desired_ver not in versions:
|
||||
print("Unsupported room version")
|
||||
exit(1)
|
||||
|
||||
def worker(room_found):
|
||||
try:
|
||||
while not room_found.value:
|
||||
room_id = globals.make_event_id().replace("$", "!")
|
||||
room_ver = globals.room_version_from_id(room_id)
|
||||
|
||||
if room_ver == desired_ver:
|
||||
print(f"!{room_id}:{config.server_name}")
|
||||
print(room_id)
|
||||
room_found.value = True
|
||||
break
|
||||
except:
|
||||
print("")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
room_found = multiprocessing.Value('b', False)
|
||||
processes = []
|
||||
|
||||
for _ in range(multiprocessing.cpu_count()):
|
||||
p = multiprocessing.Process(target=worker, args=(room_found,))
|
||||
processes.append(p)
|
||||
p.start()
|
||||
|
||||
for p in processes:
|
||||
p.join()
|
||||
|
||||
Reference in New Issue
Block a user