pantalaimon/main.py

172 lines
4.6 KiB
Python
Executable File

#!/usr/bin/env python3
import attr
import asyncio
import aiohttp
import os
import json
from aiohttp import web, ClientSession
from nio import AsyncClient, LoginResponse
from appdirs import user_data_dir
from json import JSONDecodeError
HOMESERVER = "https://localhost:8448"
@attr.s
class ProxyDaemon:
homeserver = attr.ib()
proxy = attr.ib(default=None)
ssl = attr.ib(default=None)
client_sessions = attr.ib(init=False, default=attr.Factory(dict))
default_session = attr.ib(init=False, default=None)
def get_access_token(self, request):
# type: (aiohttp.BaseRequest) -> str
"""Extract the access token from the request.
This method extracts the access token either from the query string or
from the Authorization header of the request.
Returns the access token if it was found.
"""
access_token = request.query.get("access_token", "")
if not access_token:
access_token = request.headers.get(
"Authorization",
""
).strip("Bearer ")
return access_token
async def router(self, request):
path = request.path
method = request.method
data = await request.text()
headers = request.headers
params = request.query
print(method, path, data)
session = None
token = self.get_access_token(request)
client = self.client_sessions.get(token, None)
if client:
session = client.client_session
if not session:
if not self.default_session:
self.default_session = ClientSession()
session = self.default_session
async with session.request(
method,
HOMESERVER + path,
data=data,
params=params,
headers=headers,
proxy=self.proxy,
ssl=False
) as resp:
print("Returning resp {}".format(resp))
return(web.Response(text=await resp.text()))
async def login(self, request):
try:
body = await request.json()
except JSONDecodeError:
# TODO what to do here, quaternion retries the login if we raise an
# exception here, throws an error if we send out an 400 and hangs
# if we forward it to the router() method.
print("JSON ERROR IN LOGIN")
raise
# return web.Response(
# status=400,
# text=json.dumps({
# "errcode": "M_NOT_JSON",
# "error": "Request did not contain valid JSON."
# })
# )
print("Login request")
print(body)
identifier = body.get("identifier", None)
if identifier:
user = identifier.get("user", None)
if not user:
user = body.get("user", "")
else:
user = body.get("user", "")
password = body.get("password", "")
device_id = body.get("device_id", "")
device_name = body.get("initial_device_display_name", "")
store_path = user_data_dir("pantalaimon", "")
try:
os.makedirs(store_path)
except OSError:
pass
client = AsyncClient(
HOMESERVER,
user,
device_id,
store_path=store_path,
ssl=self.ssl,
proxy=self.proxy
)
print("Logging in")
response = await client.login(password, device_name)
if isinstance(response, LoginResponse):
self.client_sessions[response.access_token] = client
else:
# TODO close the client and its session.
pass
return web.Response(
status=response.transport_response.status,
text=await response.transport_response.text()
)
async def sync(self, request):
return web.Response(
status=405,
text="Not implemented"
)
async def init():
"""Initialize the proxy and the http server."""
proxy = ProxyDaemon(HOMESERVER, proxy="http://localhost:8080", ssl=False)
app = web.Application()
app.add_routes([
web.post("/_matrix/client/r0/login", proxy.login),
web.get("/_matrix/client/r0/sync", proxy.sync),
])
app.router.add_route('*', "/" + '{proxyPath:.*}', proxy.router)
return proxy, app
def main():
loop = asyncio.get_event_loop()
proxy, app = loop.run_until_complete(init())
web.run_app(app, host="127.0.0.1", port=8081)
if __name__ == "__main__":
main()