use std::sync::Arc; use axum::extract::{Query, State}; use axum::response::{Html, Redirect}; use axum::routing::post; use axum::{Form, Router, routing::get}; use kawari::config::get_config; use kawari::login::{LoginDatabase, LoginError}; use serde::Deserialize; #[derive(Clone)] struct LoginServerState { database: Arc, } #[derive(Deserialize)] #[allow(dead_code)] struct Params { lng: String, rgn: String, isft: String, cssmode: String, isnew: String, launchver: String, } async fn top(Query(_): Query) -> Html<&'static str> { Html( "\r\n\r\n\r\n\r\n
\r\n\t\r\n\t\t\r\n\t\t\n\r\n\t\t\r\n\t\t
\r\n\t\t\t\r\n\t\t\t\r\n\t\t\t\r\n\t\t
\r\n\r\n\t\t
\r\n\t\t\t\r\n\t\t\t\r\n\t\t
\r\n\t\r\n\t\t
\r\n\t\t\t\r\n\t\t\t\r\n\t\t
\r\n\r\n\t\t\r\n\t\t
\r\n\t\t\t\r\n\t\t\t\r\n\t\t
\r\n\t\t\r\n\r\n\t\t
\r\n\t\t\t\r\n\t\t
\r\n\r\n\t
\r\n\r\n\r\n\r\n\r\n", ) } #[derive(Deserialize, Debug)] #[allow(dead_code, non_snake_case)] struct Input { _STORED_: String, sqexid: String, password: String, otppw: String, } async fn login_send( State(state): State, Form(input): Form, ) -> Html { let user = state.database.login_user(&input.sqexid, &input.password); match user { Ok(session_id) => Html(format!( "window.external.user(\"login=auth,ok,sid,{session_id},terms,1,region,2,etmadd,0,playable,1,ps3pkg,0,maxex,4,product,1\");" )), Err(err) => { // TODO: see what the official error messages are match err { LoginError::WrongUsername => { Html("window.external.user(\"login=auth,ng,err,Wrong Username\");".to_string()) } LoginError::WrongPassword => { Html("window.external.user(\"login=auth,ng,err,Wrong Password\");".to_string()) } LoginError::InternalError => Html( "window.external.user(\"login=auth,ng,err,Internal Server Error\");" .to_string(), ), } } } } #[derive(Deserialize, Debug)] #[allow(dead_code)] struct RegisterInput { username: Option, password: Option, } async fn do_register( State(state): State, Form(input): Form, ) -> Redirect { tracing::info!( "Registering with {:#?} and {:#?}!", input.username, input.password ); let Some(username) = input.username else { panic!("Expected username!"); }; let Some(password) = input.password else { panic!("Expected password!"); }; state.database.add_user(&username, &password); Redirect::to("/") } #[derive(Deserialize)] #[allow(dead_code)] struct CheckSessionParams { sid: String, } async fn check_session( State(state): State, Query(params): Query, ) -> String { if state.database.check_session(¶ms.sid) { "1".to_string() } else { "0".to_string() } } #[tokio::main] async fn main() { tracing_subscriber::fmt::init(); let state = LoginServerState { database: Arc::new(LoginDatabase::new()), }; let app = Router::new() .route("/oauth/ffxivarr/login/top", get(top)) .route("/oauth/ffxivarr/login/login.send", post(login_send)) .route("/register", post(do_register)) // TODO: make these actually private .route("/private/check_session", get(check_session)) .with_state(state); let config = get_config(); let addr = config.login.get_socketaddr(); tracing::info!("Login server started on {addr}"); axum::Server::bind(&addr) .serve(app.into_make_service()) .await .unwrap(); }