use std::time::{SystemTime, UNIX_EPOCH}; use std::collections::HashMap; use serde_json::json; use mysql_async::{self, Pool}; use hyper::{Response, Body, StatusCode}; use db::{BigInt, Member, Invite}; use crate::{http, meta, qs_param}; /* * Error handling: * All errors raisable from this module come from mysql_async and thus * are of the enum mysql_async::error::Error */ async fn valid_invite(pool: &Pool, id: BigInt) -> bool { /* * Fetches an invite from the database to check for validity */ let query = if let Ok(inv) = Invite::get(pool, id).await { match inv { db::Response::Row(data) => Some(data), _ => None } } else { None }; if let Some(invite) = query { // if expires at all if invite.expires { let now: i64 = SystemTime::now() .duration_since(UNIX_EPOCH) .expect("Systemtime fetch failed") .as_millis() as i64; // old? let mut valid_status = now < invite.id; // used? if invite.uses.is_some() && valid_status == false { valid_status = invite.uses.unwrap() <= 0; // safe unwrap since we know its Some(_) } return valid_status } // no expiry date? no problem return true } // prolly not a real id return false } async fn use_invite(pool: &Pool, code: Option) -> Option{ /* * Attempts to change the state of the current invite being provided */ use crate::auth; use crate::perms::GENERAL_NEW; let id = match code { Some(id) => id, None => 0 }; // some random comment if valid_invite(pool, id).await { let raw_secret = auth::generate_secret(); let secret = auth::encrypt_secret(&raw_secret).unwrap(); return match Member::add(pool, "Anonymous".into(), &secret, GENERAL_NEW).await { Ok(dbresponse) => { match dbresponse { db::Response::Row(mut member) => { member.secret = raw_secret; Some(member) } _ => None } }, // TODO: logggin or something idk Err(_) => None } } // The invite itself was not valid // Returning None because we couldn't actually create a proper secret to store return None; } pub async fn join(pool: &Pool, response: &mut Response, params: HashMap) { /* * Main dispatcher for dealing with an attempted join via a given invide code * */ let code = match params.get("code") { Some(val) => { // i64 because we're using unix timestamps and basically the database is dumb if let Ok(num) = (*val).to_string().parse::() { Some(num) } else { None } }, None => None }; // Returns a new member if the code is used successfully match use_invite(&pool, code).await { Some(new_account) => { http::set_json_body(response, json!({ "user": new_account, "server": meta::get_config() })) }, None => { *response.status_mut() = StatusCode::NOT_FOUND; } } } async fn allowed_perm_invite(pool: &Pool, uid: u64) -> bool { use crate::perms; return match Member::get(pool, uid).await { Ok(response) => match response { db::Response::Row(user) => perms::has_perm(user.permissions, perms::CREATE_PERM_INVITES), _ => false }, Err(e) => { eprintln!("[HTTP-DB-ERROR] {}", e); false } }; } pub async fn create(pool: &Pool, response: &mut Response, params: HashMap) { /* * Creates a new invite * Parameters required asked of the user to provide * uses : Option * expires: Option */ let id = qs_param!(params, "id", u64).unwrap(); let use_count = match params.get("uses") { Some(val) => { match (*val).to_string().parse::() { Ok(count) => Some(count), Err(_) => None } }, None => None }; let expirey_request = match params.get("expires") { Some(exp_val) => { match exp_val.to_string().parse::() { Ok(exp) => { match exp { true => allowed_perm_invite(pool, id).await, false => false } }, _ => false } } None => true }; // TODO: prolly add some kind option to set an expire time let invite = Invite::new(use_count, expirey_request); match invite.add(pool).await { Ok(_) => { // return the id of the invite // Link format from here is basically hostname.io:4536/join?code= http::set_json_body(response, serde_json::json!({"invite":invite})) }, Err(mysqle) => { println!("\tINVITES::CREATE::ERROR: {}", mysqle); *response.status_mut() = StatusCode::BAD_REQUEST; } } }