Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
8a2bae706e
|
|||
|
4603ed86ac
|
|||
|
4a6e65226e
|
|||
|
fc9787b8ec
|
|||
|
2dbec63ff7
|
|||
|
2021fc027b
|
|||
|
e0115fe8db
|
@@ -2,7 +2,9 @@ from flask import Flask, jsonify, request, redirect
|
|||||||
import vona.globals as globals
|
import vona.globals as globals
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import vona.config as config
|
import vona.config as config
|
||||||
|
import threading
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
from vona.federation import server
|
from vona.federation import server
|
||||||
from vona.custom import custom
|
from vona.custom import custom
|
||||||
@@ -24,9 +26,20 @@ app.register_blueprint(server)
|
|||||||
app.register_blueprint(apps)
|
app.register_blueprint(apps)
|
||||||
|
|
||||||
@app.before_request
|
@app.before_request
|
||||||
async def preflight():
|
async def validate_json():
|
||||||
if request.method == "OPTIONS":
|
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
|
@app.after_request
|
||||||
async def handle_logging(response):
|
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)
|
app.run(host=config.addr, port=config.port)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ server_name: str = ""
|
|||||||
signing_key: str = ""
|
signing_key: str = ""
|
||||||
support: dict = {"contacts": []}
|
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:
|
def _fatal(msg: str) -> None:
|
||||||
|
|||||||
@@ -10,8 +10,10 @@ custom = Blueprint("custom", __name__)
|
|||||||
|
|
||||||
from .conduwuit import conduwuit
|
from .conduwuit import conduwuit
|
||||||
from .dendrite import dendrite
|
from .dendrite import dendrite
|
||||||
|
from .telodendria import telo
|
||||||
from .synapse import synapse
|
from .synapse import synapse
|
||||||
|
|
||||||
custom.register_blueprint(conduwuit)
|
custom.register_blueprint(conduwuit)
|
||||||
custom.register_blueprint(dendrite)
|
custom.register_blueprint(dendrite)
|
||||||
custom.register_blueprint(synapse)
|
custom.register_blueprint(synapse)
|
||||||
|
custom.register_blueprint(telo)
|
||||||
|
|||||||
86
vona/custom/telodendria.py
Normal file
86
vona/custom/telodendria.py
Normal 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
|
||||||
|
})
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
from flask import jsonify, Response, request, send_file, abort, Blueprint
|
from flask import jsonify, Response, request, send_file, abort, Blueprint
|
||||||
from vona.config import *
|
|
||||||
import vona.globals as globals
|
import vona.globals as globals
|
||||||
import httpx
|
from vona.config import *
|
||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
import os
|
import os
|
||||||
@@ -158,10 +157,6 @@ def send_join(request, roomId) -> dict:
|
|||||||
[
|
[
|
||||||
spls["event_id"],
|
spls["event_id"],
|
||||||
spls["hashes"]
|
spls["hashes"]
|
||||||
],
|
|
||||||
[
|
|
||||||
sjoin_rule["event_id"],
|
|
||||||
sjoin_rule["hashes"]
|
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
"prev_events": [[
|
"prev_events": [[
|
||||||
@@ -196,10 +191,6 @@ def send_join(request, roomId) -> dict:
|
|||||||
[
|
[
|
||||||
spls["event_id"],
|
spls["event_id"],
|
||||||
spls["hashes"]
|
spls["hashes"]
|
||||||
],
|
|
||||||
[
|
|
||||||
sjoin_rule["event_id"],
|
|
||||||
sjoin_rule["hashes"]
|
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
"prev_events": [[
|
"prev_events": [[
|
||||||
@@ -449,10 +440,10 @@ def invite_user(data):
|
|||||||
|
|
||||||
if "event" in invite_data:
|
if "event" in invite_data:
|
||||||
if "room_version" 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({
|
return jsonify({
|
||||||
"errcode": "M_INCOMPATIBLE_ROOM_VERSION",
|
"errcode": "M_INCOMPATIBLE_ROOM_VERSION",
|
||||||
"error": "Vona only supports room version 2.",
|
"error": "Unsupported room version",
|
||||||
"room_version": invite_data["room_version"]
|
"room_version": invite_data["room_version"]
|
||||||
}), 400
|
}), 400
|
||||||
|
|
||||||
@@ -468,7 +459,10 @@ def invite_user(data):
|
|||||||
and content["membership"] == "invite"
|
and content["membership"] == "invite"
|
||||||
and event["state_key"] == f"@vona:{server_name}"
|
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({
|
return jsonify({
|
||||||
@@ -482,4 +476,4 @@ async def space_hierachy(roomId):
|
|||||||
return jsonify({
|
return jsonify({
|
||||||
"errcode": "M_NOT_FOUND",
|
"errcode": "M_NOT_FOUND",
|
||||||
"error": "Room does not exist."
|
"error": "Room does not exist."
|
||||||
})
|
}), 404
|
||||||
|
|||||||
@@ -4,11 +4,13 @@ import nacl.signing
|
|||||||
import hashlib
|
import hashlib
|
||||||
import base64
|
import base64
|
||||||
import random
|
import random
|
||||||
|
import httpx
|
||||||
import copy
|
import copy
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
|
||||||
version = "1.4.2"
|
version = "1.4.3"
|
||||||
|
http_client = httpx.Client(headers={"User-Agent": f"Vona/{version}"})
|
||||||
|
|
||||||
|
|
||||||
def canonical_json(value):
|
def canonical_json(value):
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
print("Available utils:")
|
print("Available utils:")
|
||||||
|
|
||||||
a = [
|
a = [
|
||||||
"makekey"
|
"makekey",
|
||||||
|
"joinroom"
|
||||||
]
|
]
|
||||||
|
|
||||||
for t in a:
|
for t in a:
|
||||||
|
|||||||
98
vona/utils/joinroom.py
Normal file
98
vona/utils/joinroom.py
Normal 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}")
|
||||||
Reference in New Issue
Block a user