7 Commits
1.4.2 ... 1.4.3

9 changed files with 228 additions and 19 deletions

1
TODO.md Normal file
View File

@@ -0,0 +1 @@
Nothing yet...

View File

@@ -2,7 +2,9 @@ from flask import Flask, jsonify, request, redirect
import vona.globals as globals
from datetime import datetime
import vona.config as config
import threading
import logging
import os
from vona.federation import server
from vona.custom import custom
@@ -24,9 +26,20 @@ app.register_blueprint(server)
app.register_blueprint(apps)
@app.before_request
async def preflight():
async def validate_json():
if request.method == "OPTIONS":
return "", 204
return "", 200
elif request.method in ["PUT", "POST", "PATCH"]:
if "media" in request.path:
# Don't check media uploads
return
try:
request.get_json(force=True)
except Exception as e:
return jsonify({"error": "Content not JSON.", "errcode": "M_NOT_JSON"}), 400
@app.after_request
async def handle_logging(response):
@@ -100,4 +113,16 @@ async def client():
})
def federation_self_test():
try:
resp = globals.http_client.get(f"https://{config.server_name}/.well-known/matrix/server")
resp.raise_for_status()
print("[INFO] Federation self-test OK")
except Exception as e:
print(f"[FATL] Federation self-test failed: {e}")
os._exit(1)
threading.Thread(target=federation_self_test).start()
app.run(host=config.addr, port=config.port)

View File

@@ -12,7 +12,7 @@ server_name: str = ""
signing_key: str = ""
support: dict = {"contacts": []}
_CONFIG_PATH = Path("/etc/vona/config.toml")
_CONFIG_PATH = Path(os.getenv("VONA_CONFIG", "/etc/vona/config.toml"))
def _fatal(msg: str) -> None:

View File

@@ -10,8 +10,10 @@ custom = Blueprint("custom", __name__)
from .conduwuit import conduwuit
from .dendrite import dendrite
from .telodendria import telo
from .synapse import synapse
custom.register_blueprint(conduwuit)
custom.register_blueprint(dendrite)
custom.register_blueprint(synapse)
custom.register_blueprint(telo)

View File

@@ -0,0 +1,86 @@
from flask import Blueprint, request, jsonify
from vona.globals import version
import vona.config as config
telo = Blueprint("telodendria", __name__)
# The telodendria admin API as specified by
# https://git.telodendria.org/Telodendria/Telodendria/src/branch/master/docs/user/admin/README.md
@telo.route("/_telodendria/admin/v1/restart", methods=["POST"])
@telo.route("/_telodendria/admin/v1/shutdown", methods=["POST"])
async def process_management(**kwargs):
return jsonify({})
@telo.route("/_telodendria/admin/v1/privileges/<lp>", methods=["GET", "PUT", "POST", "DELETE"])
@telo.route("/_telodendria/admin/v1/privileges/", methods=["GET", "POST"])
async def privileges(lp=None):
return jsonify({
"privileges": [
"GRANT_PRIVILEGES",
"PROC_CONTROL",
"ISSUE_TOKENS",
"DEACTIVATE",
"CONFIG",
"ALIAS",
"ALL"
]
})
@telo.route("/_telodendria/admin/v1/config", methods=["GET", "PUT", "POST"])
async def configuration():
if request.method == "GET":
return jsonify({
"listen": [
{
"tls": None,
"port": config.port,
"threads": 1,
"maxConnections": 32
}
],
"serverName": config.server_name,
"pid": "/dev/null",
"baseUrl": f"https://{config.server_name}/",
"identityServer": f"https://{config.server_name}/",
"runAs": None,
"federation": True,
"registration": config.users_can_register,
"log": {
"output": "stdout",
"level": "message",
"timestampFormat": "default",
"color": True
},
"maxCache": config.the_funny_number
})
return jsonify({"restart_required": True})
@telo.route("/_telodendria/admin/v1/stats")
async def stats():
return jsonify({
"memory_allocated": config.the_funny_number,
"version": version
})
@telo.route("/_telodendria/admin/v1/tokens/<name>", methods=["GET", "DELETE"])
@telo.route("/_telodendria/admin/v1/tokens", methods=["GET", "POST"])
async def tokens(name=None):
if request.method == "DELETE":
return jsonify({})
return jsonify({
"name": "vona",
"created_by": "vona",
"created_on": config.the_funny_number,
"expires_on": config.the_funny_number,
"used": config.the_funny_number,
"uses": config.the_funny_number
})

View File

@@ -1,7 +1,6 @@
from flask import jsonify, Response, request, send_file, abort, Blueprint
from vona.config import *
import vona.globals as globals
import httpx
from vona.config import *
import json
import time
import os
@@ -158,10 +157,6 @@ def send_join(request, roomId) -> dict:
[
spls["event_id"],
spls["hashes"]
],
[
sjoin_rule["event_id"],
sjoin_rule["hashes"]
]
],
"prev_events": [[
@@ -196,10 +191,6 @@ def send_join(request, roomId) -> dict:
[
spls["event_id"],
spls["hashes"]
],
[
sjoin_rule["event_id"],
sjoin_rule["hashes"]
]
],
"prev_events": [[
@@ -449,10 +440,10 @@ def invite_user(data):
if "event" in invite_data:
if "room_version" in invite_data:
if invite_data["room_version"] != "2":
if invite_data["room_version"] not in ["1", "2"]:
return jsonify({
"errcode": "M_INCOMPATIBLE_ROOM_VERSION",
"error": "Vona only supports room version 2.",
"error": "Unsupported room version",
"room_version": invite_data["room_version"]
}), 400
@@ -468,7 +459,10 @@ def invite_user(data):
and content["membership"] == "invite"
and event["state_key"] == f"@vona:{server_name}"
):
return jsonify({"event": globals.sign_json_without_discard(invite_data["event"]), "room_version": "2"})
return jsonify({
"event": globals.sign_json_without_discard(invite_data["event"]),
"room_version": invite_data["room_version"]
})
return jsonify({
@@ -482,4 +476,4 @@ async def space_hierachy(roomId):
return jsonify({
"errcode": "M_NOT_FOUND",
"error": "Room does not exist."
})
}), 404

View File

@@ -4,11 +4,13 @@ import nacl.signing
import hashlib
import base64
import random
import httpx
import copy
import json
import re
version = "1.4.2"
version = "1.4.3"
http_client = httpx.Client(headers={"User-Agent": f"Vona/{version}"})
def canonical_json(value):

View File

@@ -1,7 +1,8 @@
print("Available utils:")
a = [
"makekey"
"makekey",
"joinroom"
]
for t in a:

98
vona/utils/joinroom.py Normal file
View File

@@ -0,0 +1,98 @@
import vona.globals as globals
import vona.config as config
import urllib.parse
import httpx
import json
import time
http_client = globals.http_client
def get_user_input(prompt):
try:
return urllib.parse.quote(input(prompt).replace("\\n", "\n", count=32))
except Exception as e:
print(f"Error reading input: {e}")
return None
username = get_user_input("Username:\n\t")
room_id = get_user_input("Room ID:\n\t")
try:
unresolved_server_name = input("\nServer name before resolve:\n\t")
resolved_server_name = input("Server name after resolve:\n\t")
except Exception as e:
print(f"Error reading server names: {e}")
exit(1)
try:
make_join_auth = globals.make_auth_header(
unresolved_server_name,
"GET",
f"/_matrix/federation/v1/make_join/{room_id}/%40{username}%3A{config.server_name}?ver=1&ver=2&ver=3&ver=4&ver=5&ver=6&ver=7&ver=8&ver=9&ver=10&ver=11&ver=12",
)
print("\nSending make_join request..")
make_join_response = http_client.get(
f"https://{resolved_server_name}/_matrix/federation/v1/make_join/{room_id}/%40{username}%3A{config.server_name}?ver=1&ver=2&ver=3&ver=4&ver=5&ver=6&ver=7&ver=8&ver=9&ver=10&ver=11&ver=12",
headers={
"Authorization": make_join_auth
},
)
make_join_response.raise_for_status()
make_join = make_join_response.json()
except httpx.HTTPStatusError as e:
print(f"HTTP error occurred: {e.response.status_code} - {e.response.text}")
exit(1)
except json.JSONDecodeError:
print("Failed to decode JSON response.")
exit(1)
except Exception as e:
print(f"An error occurred: {e}")
exit(1)
join_event = make_join.get("event", {})
if make_join.get("room_version") in ["1", "2"]:
# NOTE: if we always make it opaque than Synapse will 500 lmao
join_event["event_id"] = globals.make_event_id()
timestamp = input("\nTimestamp (leave blank for now):\n\t")
try:
join_event["origin_server_ts"] = int(timestamp)
except ValueError:
join_event["origin_server_ts"] = int(f"{time.time() * 1000}".split(".")[0])
signed_join = globals.hash_and_sign_event(join_event)
try:
send_join_auth = globals.make_auth_header(
unresolved_server_name,
"PUT",
f"/_matrix/federation/v2/send_join/{room_id}/%24doesntmatter?omit_members=true",
signed_join,
)
send_join_response = http_client.put(
f"https://{resolved_server_name}/_matrix/federation/v2/send_join/{room_id}/%24doesntmatter?omit_members=true",
headers={
"Authorization": send_join_auth
},
json=signed_join,
)
send_join_response.raise_for_status()
print("\nSuccess :)")
except httpx.HTTPStatusError as e:
print(
f"HTTP error occurred during send_join: {e.response.status_code} - {e.response.text}"
)
except Exception as e:
print(f"An error occurred during send_join: {e}")