use mysql_async::Pool; use hyper::{Response, Body, HeaderMap, StatusCode}; use hyper::body::to_bytes; use serde_json::json; use std::collections::HashMap; use crate::http::set_json_body; use crate::perms; use crate::qs_param; use db::Message; pub async fn get_by_time(pool: &Pool, response: &mut Response, params: HashMap) { /* * Has a ton of required parameters just be warned * @channel: channel id we're looking at * @start-time: how long ago do we start searching * @end-time: how long ago do we stop searching * { * "channel_id": 1, * "start_time": unix_now - 24 hours * "end_time": unix_now - 23 hours * } * */ let channel_id = qs_param!(params, "channel_id", u64); let start_time = qs_param!(params, "start_time", i64); let end_time = qs_param!(params, "end_time", i64); let limit = qs_param!(params, "limit", u64); // TODO: flatten me mommy if let (Some(channel), Some(start), Some(end)) = (channel_id, start_time, end_time) { match Message::get_time_range(pool, channel, start, end, limit).await { Ok(db_response) => { match db_response { // this absolute lack of data streaming is prolly gonna suck like // a whore in hell week for performance but lets pretend servers don't get massive db::Response::Set(messages) => set_json_body(response, json!({"messages": messages})), db::Response::RestrictedInput(_/*error message to log*/) => { *response.status_mut() = StatusCode::BAD_REQUEST; } _ => { *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; } }; }, Err(e) => { eprintln!("{}", e); *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; } }; } else { *response.status_mut() = StatusCode::BAD_REQUEST; } } pub async fn send_message(pool: &Pool, response: &mut Response, body: Body, headers: HeaderMap, params: HashMap) { /* * Message content is sent in the message body * @channel_id: channel id that we're going to send a message to * @content-type in the headers * TODO: more features here because send_message is a large handler */ use db::Response::*; use db::Member; // NOTE: auth module guarantees that id will be present so the unwrap is safe let uid = qs_param!(params, "id", u64).unwrap(); let ctype: Option<&str> = match headers.get("Content-Type") { Some(hval) => { match hval.to_str() { Ok(s) => Some(s), _ => None } }, None => None }; let permissions = match Member::get(pool, uid).await { Ok(dbresponse) => match dbresponse { Row(user) => user.permissions, _ => 0 }, Err(e) => { eprintln!("[DB-SQL] {}",e ); 0 } }; if perms::has_perm(permissions, perms::SEND_MESSAGES) == false { *response.status_mut() = StatusCode::BAD_REQUEST; return; } let channel_id = qs_param!(params, "channel_id", u64); // Black magic let body_bytes: &[u8] = &to_bytes(body).await.unwrap(); // yolo let content = String::from_utf8_lossy(body_bytes); // 400 on empty bodies or missing channel id's if content.len() == 0 || channel_id.is_none() || ctype.is_none() { *response.status_mut() = StatusCode::BAD_REQUEST; } else { // block away wrong content types const CONTENT_TYPES: [&'static str;7] = [ "text/plain", "image/png", "image/jpeg", "image/jpg", "application/webm", "application/mp4", "application/mp3" ]; if CONTENT_TYPES.contains(&ctype.unwrap()) == false { *response.status_mut() = StatusCode::BAD_REQUEST; } else { // Safe unwrap with ctype - its container type is checked prior match db::Message::send(pool, &content, ctype.unwrap(), channel_id.unwrap(), uid).await { Ok(Row(msg)) => { use crate::rtc; rtc::new_message(pool, msg).await; }, Ok(RestrictedInput(_msg)) => *response.status_mut() = StatusCode::BAD_REQUEST, Ok(Other(msg)) => { eprintln!("{}", msg); *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; }, Ok(_) => {}, // Can't happen because no branches return remaining variants Err(e) => { eprintln!("/message/send error: {}", e); *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR } } } } } pub async fn recent_messages(pool: &Pool, response: &mut Response, params: HashMap) { let limit = qs_param!(params, "limit", i32); let channel_id = qs_param!(params, "channel_id", u64); if channel_id.is_some() && limit.is_some() { // NOTE: using match here to capture+log the error from the database match Message::last_n(pool, limit.unwrap(), channel_id.unwrap()).await { Ok(dbresp) => { match dbresp { // 200 db::Response::Set(messages) => set_json_body(response, json!({"messages": messages})), // 404 - assuming the request channel does not exist db::Response::Empty => *response.status_mut() = StatusCode::NOT_FOUND, // 400 db::Response::RestrictedInput(msg) => { *response.status_mut() = StatusCode::BAD_REQUEST; set_json_body(response, json!({"error": msg})); }, // unreachable branch _ => {} } }, Err(e) => { eprintln!("{}", e); } } } else { } }