use mtp::codec::{CommunicationType, CommunicationValue, DataType, DataValue, TypeMap}; use std::str::FromStr; use std::sync::Arc; use std::time::Duration; use tokio::sync::RwLock; use uuid::Uuid; use crate::anonymous_clients::anonymous_manager::{self, generate_username}; use crate::app_state::AppState; use crate::calls::call_group::call_invite_secret_from_cv; use crate::rho::connection::{ GeneralConnection, MtpReceiver, MtpSender, OptionalDataValueCompat, RequiredMtpFields, }; use crate::util::data_type_id; use crate::util::logger::PrintType; use crate::{log_cv_in, log_cv_out, log_out}; pub struct AnonymousClientConnection { pub state: Arc, user_id: u64, pub sender: Arc, pub receiver: Arc, pub interested_users: Arc>>, is_open: Arc>, pub user_name: Arc>, pub display_name: Arc>, pub avatar: Arc>, message_slots: Arc, } impl AnonymousClientConnection { pub async fn from_general(general: Arc, user_id: u64) -> Arc { let username: String = generate_username(); Arc::new(Self { state: general.state.clone(), user_id: user_id, interested_users: Arc::new(RwLock::new(Vec::new())), is_open: Arc::new(RwLock::new(true)), sender: general.sender.clone(), receiver: general.receiver.clone(), user_name: Arc::new(RwLock::new(username.to_lowercase())), display_name: Arc::new(RwLock::new(username)), avatar: Arc::new(RwLock::new(String::new())), message_slots: Arc::new(tokio::sync::Semaphore::new(32)), }) } pub fn start(self: Arc) { let self_clone = self.clone(); tokio::spawn(async move { while let Ok(cv) = self_clone.receiver.receive().await { self_clone.clone().handle_message(cv).await; } self_clone.handle_close().await; }); } /// Get the user ID pub fn get_user_id(&self) -> u64 { self.user_id } /// Get the user name pub async fn get_user_name(&self) -> String { self.user_name.read().await.clone() } /// Get the display name pub async fn get_display_name(&self) -> String { self.display_name.read().await.clone() } pub async fn set_display_name(&self, display: String) { *self.display_name.write().await = display; } /// Get the avatar pub async fn get_avatar(&self) -> String { self.avatar.read().await.clone() } /// Send a CommunicationValue to the client pub async fn send_message(self: Arc, cv: &CommunicationValue) { if !*self.is_open.read().await { log_out!( self.user_id as i64, PrintType::Client, "Attempted to send message to a closed connection." ); return; } log_cv_out!(PrintType::Client, &cv); if let Err(e) = self.sender.send(&cv).await { log_out!( self.user_id as i64, PrintType::Client, "Send failed: {:?}", e ); } } /// Handle incoming message from client pub async fn handle_message(self: Arc, cv: CommunicationValue) { let Ok(permit) = self.message_slots.clone().acquire_owned().await else { return; }; tokio::spawn(async move { let _permit = permit; let message_id = match cv.require_id() { Ok(message_id) => message_id, Err(error) => { let response = CommunicationValue::new(CommunicationType::ErrorInvalidData).without_id(); log_out!( self.user_id as i64, PrintType::Client, "Rejected malformed message: {}", error ); self.send_message(&response).await; return; } }; log_cv_in!(PrintType::Client, &cv); if cv.is_type(CommunicationType::Relay) { self.send_error_response(message_id, CommunicationType::ErrorNotAuthenticated) .await; return; } if cv.is_type(CommunicationType::Identification) { let call_id = Uuid::parse_str(cv.get_data(DataType::CallId).as_str().unwrap_or("")) .unwrap_or(Uuid::new_v4()); let call = if let Some(call) = self.state.call_manager.get_call(call_id).await { if call.is_anonymous().await { call } else { self.send_error_response( message_id, CommunicationType::ErrorNotAuthenticated, ) .await; return; } } else { self.send_error_response(message_id, CommunicationType::ErrorNotAuthenticated) .await; return; }; let mut invited = Vec::new(); for call_invitee in call.members.read().await.clone() { let Ok(call_invitee_cv) = self .state .omega .clone() .await_response( &CommunicationValue::new(CommunicationType::GetUserData) .add_typed_default( DataType::UserId, DataValue::SignedNumber(call_invitee.user_id.into()), ), Some(Duration::from_secs(2)), ) .await else { continue; }; let mut json_invitee = Vec::new(); let _ = json_invitee.push(( DataType::UserId, call_invitee_cv .get_data(DataType::UserId) .cloned() .unwrap_or(DataValue::Null), )); let _ = json_invitee.push(( DataType::Username, call_invitee_cv .get_data(DataType::Username) .cloned() .unwrap_or(DataValue::Null), )); let _ = json_invitee.push(( DataType::Display, call_invitee_cv .get_data(DataType::Display) .cloned() .unwrap_or(DataValue::Null), )); let _ = json_invitee.push(( DataType::Avatar, call_invitee_cv .get_data(DataType::Avatar) .cloned() .unwrap_or(DataValue::Null), )); let _ = invited.push(DataValue::Container( json_invitee .iter() .map(|(k, v)| (data_type_id(*k, &TypeMap::latest()), v.clone())) .collect(), )); } let token = match call.create_anonymous_token(self.get_user_id()).await { Ok(Some(token)) => token, Ok(None) | Err(_) => return, }; let mut serialized = Vec::new(); let _ = serialized.push((DataType::CallId, DataValue::Str(call_id.to_string()))); let _ = serialized.push((DataType::CallInvited, DataValue::Array(invited.clone()))); let _ = serialized.push((DataType::CallMembers, DataValue::Array(invited))); let _ = serialized.push((DataType::CallToken, DataValue::Str(token))); self.clone() .send_message( &&CommunicationValue::new(CommunicationType::IdentificationResponse) .with_id(message_id) .add_typed_default( DataType::UserId, DataValue::SignedNumber(self.user_id.into()), ) .add_typed_default( DataType::Username, DataValue::Str(self.clone().get_user_name().await), ) .add_typed_default( DataType::Display, DataValue::Str(self.get_display_name().await), ) .add_typed_default( DataType::Avatar, DataValue::Str(self.get_avatar().await), ) .add_typed_default( DataType::CallState, DataValue::Container( serialized .iter() .map(|(k, v)| { (data_type_id(*k, &TypeMap::latest()), v.clone()) }) .collect(), ), ), ) .await; } // Presence is account-scoped and anonymous sessions have no // persisted account preference to change. if cv.is_type(CommunicationType::ClientChanged) { self.send_error_response(message_id, CommunicationType::ErrorNoUserId) .await; return; } // Handle call invites if cv.is_type(CommunicationType::CallInvite) { self.handle_call_invite(cv).await; return; } // Handle get call requests if cv.is_type(CommunicationType::CallToken) { self.handle_get_call(cv).await; return; } if cv.is_type(CommunicationType::CallDisconnectUser) { self.handle_call_disconnect_user(cv).await; return; } if cv.is_type(CommunicationType::CallTimeoutUser) { self.handle_call_timeout_user(cv).await; return; } if cv.is_type(CommunicationType::ChangeUserData) { if let Some(display_name) = cv.get_data(DataType::Display).as_str() { let _ = self.set_display_name(display_name.to_string()).await; } return; } if cv.is_type(CommunicationType::GetUserData) { if let Some(anonymous) = { if let Some(user_id) = cv.get_data(DataType::UserId).as_signed_number() { anonymous_manager::get_anonymous_user(user_id as u64).await } else if let Some(username) = cv.get_data(DataType::Username).as_str() { anonymous_manager::get_anonymous_user_by_name(username.to_string()).await } else { None } } { let response = CommunicationValue::new(CommunicationType::GetUserData) .with_id(message_id) .add_typed_default( DataType::Username, DataValue::Str(anonymous.get_user_name().await), ) .add_typed_default( DataType::UserId, DataValue::SignedNumber(anonymous.user_id.into()), ) .add_typed_default( DataType::Display, DataValue::Str(anonymous.get_display_name().await), ) .add_typed_default( DataType::UserState, DataValue::Str("online".to_string()), ) .add_typed_default( DataType::Avatar, DataValue::Str(anonymous.get_avatar().await), ); self.send_message(&response).await; return; } } if cv.is_type(CommunicationType::GetUserData) || cv.is_type(CommunicationType::GetIotaData) { self.handle_omega_forward(cv).await; return; } }); } async fn handle_omega_forward(self: Arc, cv: CommunicationValue) { let client_for_closure = self.clone(); tokio::spawn(async move { let response_cv = self .state .omega .clone() .await_response(&cv.with_sender(self.user_id), Some(Duration::from_secs(20))) .await; if let Ok(response_cv) = response_cv { client_for_closure.send_message(&response_cv).await; } }); } /// Handle call invite async fn handle_call_invite(self: Arc, cv: CommunicationValue) { let Ok(message_id) = cv.require_id() else { return; }; let receiver_id: i64 = cv.get_data(DataType::ReceiverId).as_number().unwrap_or(0) as i64; if receiver_id == 0 { self.send_error_response(message_id, CommunicationType::ErrorNoUserId) .await; return; } let call_id = match cv.get_data(DataType::CallId) { Some(DataValue::Str(id_str)) => match Uuid::parse_str(id_str) { Ok(id) => id, Err(_) => { self.send_error_response(message_id, CommunicationType::ErrorInvalidCallId) .await; return; } }, _ => { self.send_error_response(message_id, CommunicationType::ErrorNoCallId) .await; return; } }; let secret = match call_invite_secret_from_cv(&cv) { Some(secret) => secret, None => { self.send_error_response(message_id, CommunicationType::BadRequest) .await; return; } }; let invited = self .state .call_manager .add_invite(call_id, self.user_id, receiver_id as u64, secret.clone()) .await; if !invited { self.send_error_response(message_id, CommunicationType::ErrorInvalidCallId) .await; return; } if !self .state .call_manager .should_forward_invite(self.user_id, receiver_id as u64) { let response = CommunicationValue::new(CommunicationType::Success).with_id(message_id); self.send_message(&response).await; return; } // Find target RhoConnection let target_rho = match self.state.rho.get_for_user(receiver_id).await { Some(rho) => rho, _ => { // Get sender user ID let sender_id = self.get_user_id(); // User is offline - send push notification for call invite let push_cv = CommunicationValue::new(CommunicationType::PushNotification) .with_receiver(receiver_id as u64) .add_typed_default( DataType::SenderId, DataValue::SignedNumber(sender_id.into()), ) .add_typed_default(DataType::CallId, DataValue::Str(call_id.to_string())) .add_typed_default( DataType::Notifications, DataValue::Str("call_invite".to_string()), ); let omega_conn = self.state.omega.clone(); // Send fire-and-forget, don't await to avoid blocking tokio::spawn(async move { let _ = omega_conn.send_message(&push_cv).await; }); let error_cv = CommunicationValue::new(CommunicationType::ErrorNotFound) .with_id(message_id) .add_typed_default( DataType::ReceiverId, DataValue::SignedNumber(receiver_id.into()), ); self.send_message(&error_cv).await; return; } }; // Get sender user ID let sender_id = self.get_user_id(); // Create and send call distribution message let forward = CommunicationValue::new(CommunicationType::CallInvite) .with_receiver(receiver_id as u64) .with_sender(sender_id) .add_typed_default(DataType::CallSecret, secret.to_data_value()) .add_typed_default(DataType::CallId, DataValue::Str(call_id.to_string())) .add_typed_default( DataType::ReceiverId, DataValue::SignedNumber(receiver_id.into()), ) .add_typed_default( DataType::SenderId, DataValue::SignedNumber(sender_id.into()), ); target_rho.message_to_client(forward).await; let response = CommunicationValue::new(CommunicationType::Success).with_id(message_id); self.send_message(&response).await; } /// Handle get call request async fn handle_get_call(self: Arc, cv: CommunicationValue) { let Ok(message_id) = cv.require_id() else { return; }; let user_id = self.get_user_id(); let call_id = match cv.get_data(DataType::CallId) { Some(DataValue::Str(id_str)) => match Uuid::parse_str(id_str) { Ok(id) => id, Err(_) => { self.send_error_response(message_id, CommunicationType::ErrorInvalidCallId) .await; return; } }, _ => { self.send_error_response(message_id, CommunicationType::ErrorNoCallId) .await; return; } }; match self .state .call_manager .get_call_token(user_id, call_id) .await { Ok(token) => { let response = CommunicationValue::new(CommunicationType::CallToken) .with_id(message_id) .with_receiver(user_id) .add_typed_default(DataType::CallToken, DataValue::Str(token)); self.send_message(&response).await; } Err(error) => { log::warn!( "Unable to create anonymous call token for {}: {}", call_id, error ); let error_cv = CommunicationValue::new(CommunicationType::ErrorNoCallId) .with_id(message_id) .add_typed_default(DataType::CallId, DataValue::Str(call_id.to_string())); self.send_message(&error_cv).await; } } } async fn handle_call_timeout_user(self: Arc, cv: CommunicationValue) { let Ok(message_id) = cv.require_id() else { return; }; let Ok(call_id) = Uuid::from_str(cv.get_data(DataType::CallId).as_str().unwrap_or("")) else { self.send_error_response(message_id, CommunicationType::ErrorInvalidCallId) .await; return; }; let user_id = cv .get_data(DataType::UserId) .as_signed_number() .unwrap_or(0); let untill = cv .get_data(DataType::Untill) .as_signed_number() .unwrap_or(0); let Some(call) = self.state.call_manager.get_call(call_id).await else { self.send_error_response(message_id, CommunicationType::ErrorNotFound) .await; return; }; let Some(caller) = call.get_caller(self.get_user_id()).await else { self.send_error_response(message_id, CommunicationType::ErrorInvalidUserId) .await; return; }; if caller.has_admin() { if let Some(target) = call.get_caller(user_id as u64).await { target.set_timeout(untill as i64).await; } } } async fn handle_call_disconnect_user(self: Arc, cv: CommunicationValue) { let Ok(message_id) = cv.require_id() else { return; }; let Ok(call_id) = Uuid::from_str(cv.get_data(DataType::CallId).as_str().unwrap_or("")) else { self.send_error_response(message_id, CommunicationType::ErrorInvalidCallId) .await; return; }; let user_id = cv .get_data(DataType::UserId) .as_signed_number() .unwrap_or(0); let Some(call) = self.state.call_manager.get_call(call_id).await else { self.send_error_response(message_id, CommunicationType::ErrorNotFound) .await; return; }; let Some(caller) = call.get_caller(self.get_user_id()).await else { self.send_error_response(message_id, CommunicationType::ErrorInvalidUserId) .await; return; }; if caller.has_admin() { call.remove_caller(user_id as u64).await; } } /// Send error response async fn send_error_response(self: Arc, message_id: u32, error_type: CommunicationType) { let error = CommunicationValue::new(error_type).with_id(message_id); self.send_message(&error).await; } /// Close the connection #[allow(unused)] pub async fn close(&self) { let mut is_open_guard = self.is_open.write().await; if !*is_open_guard { return; } *is_open_guard = false; let _ = self.sender.close(); } #[allow(dead_code)] /// Set interested users list pub async fn set_interested_users(self: Arc, interested_ids: Vec) { let mut interested_guard = self.interested_users.write().await; *interested_guard = interested_ids; } #[allow(dead_code)] pub async fn get_interested_users(self: Arc) -> Vec { let interested_guard = self.interested_users.read().await; interested_guard.clone() } #[allow(dead_code)] /// Check if interested in a user and send notification pub async fn are_you_interested(self: Arc, user_id: i64, user_status: &str) { let interested_guard = self.clone().get_interested_users().await; if interested_guard.contains(&user_id) { let status = if user_status == "user_invisible" { "user_offline" } else { user_status }; let notification = CommunicationValue::new(CommunicationType::ClientChanged) .add_typed_default(DataType::UserId, DataValue::Str(user_id.to_string())) .add_typed_default(DataType::UserState, DataValue::Str(status.to_string())); self.send_message(¬ification).await; } } /// Handle connection close pub async fn handle_close(&self) { // TODO delete temp user } } // Implement Clone to make it easier to work with Arc impl Clone for AnonymousClientConnection { fn clone(&self) -> Self { Self { state: self.state.clone(), sender: Arc::clone(&self.sender), receiver: Arc::clone(&self.receiver), user_id: self.user_id, interested_users: Arc::clone(&self.interested_users), is_open: Arc::clone(&self.is_open), user_name: Arc::clone(&self.user_name), display_name: Arc::clone(&self.display_name), avatar: Arc::clone(&self.avatar), message_slots: Arc::clone(&self.message_slots), } } }