diff --git a/Cargo.lock b/Cargo.lock index 309aa6d6..5ecd6de0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2475,7 +2475,7 @@ dependencies = [ [[package]] name = "revolt" -version = "0.3.3-alpha.8" +version = "0.4.0-alpha.0" dependencies = [ "async-std", "async-tungstenite", diff --git a/Cargo.toml b/Cargo.toml index 18b51b50..3a8eb6f0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,49 +1,44 @@ [package] name = "revolt" -version = "0.3.3-alpha.8" +version = "0.4.0-alpha.0" authors = ["Paul Makles "] edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +regex = "1" +md5 = "0.7.0" +log = "0.4.11" +ulid = "0.4.1" +rand = "0.7.3" +time = "0.2.16" base64 = "0.13.0" +dotenv = "0.15.0" futures = "0.3.8" +chrono = "0.4.15" +num_enum = "0.5.1" impl_ops = "0.1.1" web-push = "0.7.2" -many-to-many = "0.1.2" -ctrlc = { version = "3.0", features = ["termination"] } -async-std = { version = "1.8.0", features = ["tokio02", "attributes"] } -async-tungstenite = { version = "0.10.0", features = ["async-std-runtime"] } -rauth = { git = "https://gitlab.insrt.uk/insert/rauth", rev = "c52758a5087cd035b0ed9c6eacc942ba5468d2ce" } - -hive_pubsub = { version = "0.4.3", features = ["mongo"] } -rocket_cors = { git = "https://github.com/insertish/rocket_cors", branch = "master" } -rocket_prometheus = { git = "https://github.com/insertish/rocket_prometheus", rev = "3d825aedb42793246c306a81fe67c5b187948983" } -rocket_contrib = { git = "https://github.com/SergioBenitez/Rocket", rev = "031948c1daaa146128d8a435be116476f2adde00" } -rocket = { git = "https://github.com/SergioBenitez/Rocket", rev = "031948c1daaa146128d8a435be116476f2adde00", default-features = false } -mongodb = { version = "1.1.1", features = ["tokio-runtime"], default-features = false } - once_cell = "1.4.1" -dotenv = "0.15.0" -ulid = "0.4.1" - +bitfield = "0.13.2" +env_logger = "0.7.1" +serde_json = "1.0.57" +lazy_static = "1.4.0" +urlencoding = "1.1.1" +many-to-many = "0.1.2" +lettre = "0.10.0-alpha.1" +snafu = { version = "0.6.9" } +reqwest = { version = "0.10.8", features = ["json"] } serde = { version = "1.0.115", features = ["derive"] } validator = { version = "0.11", features = ["derive"] } -snafu = { version = "0.6.9" } -serde_json = "1.0.57" -bitfield = "0.13.2" - -reqwest = { version = "0.10.8", features = ["json"] } -urlencoding = "1.1.1" -lazy_static = "1.4.0" -num_enum = "0.5.1" -chrono = "0.4.15" -time = "0.2.16" -rand = "0.7.3" -md5 = "0.7.0" -regex = "1" - -lettre = "0.10.0-alpha.1" -env_logger = "0.7.1" -log = "0.4.11" +ctrlc = { version = "3.0", features = ["termination"] } +hive_pubsub = { version = "0.4.3", features = ["mongo"] } +async-std = { version = "1.8.0", features = ["tokio02", "attributes"] } +async-tungstenite = { version = "0.10.0", features = ["async-std-runtime"] } +rocket_cors = { git = "https://github.com/insertish/rocket_cors", branch = "master" } +mongodb = { version = "1.1.1", features = ["tokio-runtime"], default-features = false } +rauth = { git = "https://gitlab.insrt.uk/insert/rauth", rev = "c52758a5087cd035b0ed9c6eacc942ba5468d2ce" } +rocket_contrib = { git = "https://github.com/SergioBenitez/Rocket", rev = "031948c1daaa146128d8a435be116476f2adde00" } +rocket_prometheus = { git = "https://github.com/insertish/rocket_prometheus", rev = "3d825aedb42793246c306a81fe67c5b187948983" } +rocket = { git = "https://github.com/SergioBenitez/Rocket", rev = "031948c1daaa146128d8a435be116476f2adde00", default-features = false } diff --git a/src/database/entities/message.rs b/src/database/entities/message.rs index 1bb174e1..06913b28 100644 --- a/src/database/entities/message.rs +++ b/src/database/entities/message.rs @@ -179,7 +179,7 @@ impl Message { builder.set_vapid_signature(signature); builder.set_payload(ContentEncoding::AesGcm, enc.as_bytes()); let m = builder.build().unwrap(); - let response = client.send(m).await.ok(); + client.send(m).await.ok(); } } diff --git a/src/database/permissions/channel.rs b/src/database/permissions/channel.rs index b4096a34..e0c9c460 100644 --- a/src/database/permissions/channel.rs +++ b/src/database/permissions/channel.rs @@ -12,6 +12,7 @@ pub enum ChannelPermission { View = 1, SendMessage = 2, ManageMessages = 4, + VoiceCall = 8, } bitfield! { @@ -20,6 +21,7 @@ bitfield! { pub get_view, _: 31; pub get_send_message, _: 30; pub get_manage_messages, _: 29; + pub get_voice_call, _: 28; } impl_op_ex!(+ |a: &ChannelPermission, b: &ChannelPermission| -> u32 { *a as u32 | *b as u32 }); @@ -36,9 +38,7 @@ impl<'a> PermissionCalculator<'a> { match channel { Channel::SavedMessages { user: owner, .. } => { if &self.perspective.id == owner { - Ok(ChannelPermission::View - + ChannelPermission::SendMessage - + ChannelPermission::ManageMessages) + Ok(u32::MAX) } else { Ok(0) } @@ -54,7 +54,7 @@ impl<'a> PermissionCalculator<'a> { let perms = self.for_user(recipient).await?; if perms.get_send_message() { - return Ok(ChannelPermission::View + ChannelPermission::SendMessage); + return Ok(ChannelPermission::View + ChannelPermission::SendMessage + ChannelPermission::VoiceCall); } return Ok(ChannelPermission::View as u32); @@ -69,7 +69,7 @@ impl<'a> PermissionCalculator<'a> { .find(|x| *x == &self.perspective.id) .is_some() { - Ok(ChannelPermission::View + ChannelPermission::SendMessage) + Ok(ChannelPermission::View + ChannelPermission::SendMessage + ChannelPermission::VoiceCall) } else { Ok(0) } diff --git a/src/routes/channels/join_call.rs b/src/routes/channels/join_call.rs new file mode 100644 index 00000000..84c113d1 --- /dev/null +++ b/src/routes/channels/join_call.rs @@ -0,0 +1,65 @@ +use crate::database::*; +use crate::util::result::{Error, Result}; +use crate::util::variables::{USE_VOSO, VOSO_URL, VOSO_MANAGE_TOKEN}; + +use serde::{Serialize, Deserialize}; +use rocket_contrib::json::JsonValue; + +#[derive(Serialize, Deserialize)] +struct CreateUserResponse { + token: String +} + +#[post("//join_call")] +pub async fn req(user: User, target: Ref) -> Result { + if !*USE_VOSO { + return Err(Error::VosoUnavailable) + } + + let target = target.fetch_channel().await?; + let perm = permissions::PermissionCalculator::new(&user) + .with_channel(&target) + .for_channel() + .await?; + + if !perm.get_voice_call() { + return Err(Error::MissingPermission) + } + + // To join a call: + // - Check if the room exists. + // - If not, create it. + let client = reqwest::Client::new(); + if let Err(error) = client + .get(&format!("{}/room/{}", *VOSO_URL, target.id())) + .header(reqwest::header::AUTHORIZATION, VOSO_MANAGE_TOKEN.to_string()) + .send() + .await { + if error.status() == Some(reqwest::StatusCode::NOT_FOUND) { + if let Err(_) = client + .post(&format!("{}/room/{}", *VOSO_URL, target.id())) + .header(reqwest::header::AUTHORIZATION, VOSO_MANAGE_TOKEN.to_string()) + .send() + .await { + return Err(Error::VosoUnavailable) + } + } else { + return Err(Error::VosoUnavailable) + } + } + + // Then create a user for the room. + if let Ok(response) = client + .post(&format!("{}/room/{}/user/{}", *VOSO_URL, target.id(), user.id)) + .header(reqwest::header::AUTHORIZATION, VOSO_MANAGE_TOKEN.to_string()) + .send() + .await { + let res: CreateUserResponse = response.json() + .await + .map_err(|_| Error::InvalidOperation)?; + + Ok(json!(res)) + } else { + Err(Error::VosoUnavailable) + } +} diff --git a/src/routes/channels/mod.rs b/src/routes/channels/mod.rs index b8f37fe9..180ceef0 100644 --- a/src/routes/channels/mod.rs +++ b/src/routes/channels/mod.rs @@ -5,6 +5,7 @@ mod fetch_channel; mod group_add_member; mod group_create; mod group_remove_member; +mod join_call; mod message_delete; mod message_edit; mod message_fetch; @@ -24,6 +25,7 @@ pub fn routes() -> Vec { message_delete::req, group_create::req, group_add_member::req, - group_remove_member::req + group_remove_member::req, + join_call::req, ] } diff --git a/src/routes/root.rs b/src/routes/root.rs index 0bb35a2c..4dbee031 100644 --- a/src/routes/root.rs +++ b/src/routes/root.rs @@ -1,6 +1,6 @@ use crate::util::variables::{ AUTUMN_URL, DISABLE_REGISTRATION, EXTERNAL_WS_URL, HCAPTCHA_SITEKEY, INVITE_ONLY, USE_AUTUMN, - USE_EMAIL, USE_HCAPTCHA, VAPID_PUBLIC_KEY, + USE_EMAIL, USE_HCAPTCHA, VAPID_PUBLIC_KEY, USE_VOSO, VOSO_URL }; use mongodb::bson::doc; @@ -9,7 +9,7 @@ use rocket_contrib::json::JsonValue; #[get("/")] pub async fn root() -> JsonValue { json!({ - "revolt": "0.3.3-alpha.8", + "revolt": "0.4.0-alpha.0", "features": { "registration": !*DISABLE_REGISTRATION, "captcha": { @@ -21,6 +21,10 @@ pub async fn root() -> JsonValue { "autumn": { "enabled": *USE_AUTUMN, "url": *AUTUMN_URL + }, + "voso": { + "enabled": *USE_VOSO, + "url": *VOSO_URL } }, "ws": *EXTERNAL_WS_URL, diff --git a/src/util/result.rs b/src/util/result.rs index 7fcbf8d0..e13a25a7 100644 --- a/src/util/result.rs +++ b/src/util/result.rs @@ -69,6 +69,8 @@ pub enum Error { InvalidOperation, #[snafu(display("Already created an object with this nonce."))] DuplicateNonce, + #[snafu(display("Voso is not enabled on this instance."))] + VosoUnavailable, #[snafu(display("This request had no effect."))] NoEffect, } @@ -107,6 +109,7 @@ impl<'r> Responder<'r, 'static> for Error { Error::InvalidOperation => Status::BadRequest, Error::TooManyIds => Status::BadRequest, Error::DuplicateNonce => Status::Conflict, + Error::VosoUnavailable => Status::BadRequest, Error::NoEffect => Status::Ok, }; diff --git a/src/util/variables.rs b/src/util/variables.rs index 19b72491..7ab47807 100644 --- a/src/util/variables.rs +++ b/src/util/variables.rs @@ -15,8 +15,14 @@ lazy_static! { env::var("REVOLT_APP_URL").unwrap_or_else(|_| "https://app.revolt.chat".to_string()); pub static ref EXTERNAL_WS_URL: String = env::var("REVOLT_EXTERNAL_WS_URL").expect("Missing REVOLT_EXTERNAL_WS_URL environment variable."); + pub static ref AUTUMN_URL: String = env::var("AUTUMN_PUBLIC_URL").unwrap_or_else(|_| "https://example.com".to_string()); + pub static ref VOSO_URL: String = + env::var("VOSO_PUBLIC_URL").unwrap_or_else(|_| "https://example.com".to_string()); + pub static ref VOSO_MANAGE_TOKEN: String = + env::var("VOSO_MANAGE_TOKEN").unwrap_or_else(|_| "0".to_string()); + pub static ref HCAPTCHA_KEY: String = env::var("REVOLT_HCAPTCHA_KEY").unwrap_or_else(|_| "0x0000000000000000000000000000000000000000".to_string()); pub static ref HCAPTCHA_SITEKEY: String = @@ -39,6 +45,7 @@ lazy_static! { pub static ref USE_HCAPTCHA: bool = env::var("REVOLT_HCAPTCHA_KEY").is_ok(); pub static ref USE_PROMETHEUS: bool = env::var("REVOLT_ENABLE_PROMETHEUS").map_or(false, |v| v == "1"); pub static ref USE_AUTUMN: bool = env::var("AUTUMN_PUBLIC_URL").is_ok(); + pub static ref USE_VOSO: bool = env::var("VOSO_PUBLIC_URL").is_ok() && env::var("VOSO_MANAGE_TOKEN").is_ok(); // SMTP Settings pub static ref SMTP_HOST: String =